From e7cedff48fb6245836e42eb325708f1bccd9814f Mon Sep 17 00:00:00 2001 From: arnold Date: Fri, 26 May 2023 14:12:57 -0400 Subject: [PATCH] Delete account from inside of wallet. Fixes #56 Change wording of confirmation dialog. Fixes #64 --- src/components/ConfirmationDialog.tsx | 22 +++++---- src/components/styles/StyledButton.styles.ts | 8 +-- src/constants/Text.ts | 4 ++ src/navigation/index.tsx | 4 +- src/screens/ProfileScreen/ProfileScreen.tsx | 41 ++++++++++++++-- src/services/LTO.service.ts | 51 ++++++++++++-------- 6 files changed, 91 insertions(+), 39 deletions(-) diff --git a/src/components/ConfirmationDialog.tsx b/src/components/ConfirmationDialog.tsx index 1ce39db..56012c6 100644 --- a/src/components/ConfirmationDialog.tsx +++ b/src/components/ConfirmationDialog.tsx @@ -5,17 +5,19 @@ import { ButtonContainer } from './styles/ConfirmationDialog.styles' export default function ConfirmationDialog(props: { visible: boolean, - message: string, + message?: string, onCancel: () => void, onPress: () => void, titleLabel?: string, cancelButtonLabel?: string, - continueButtonLabel?: string + continueButtonLabel?: string, + danger: boolean, + children?: React.ReactNode }): JSX.Element { const titleLabel = props.titleLabel || 'Confirm:' - const cancelButtonLabel = props.cancelButtonLabel || 'Cancel' - const continueButtonLabel = props.continueButtonLabel || 'Continue' + const cancelButtonLabel = props.cancelButtonLabel || 'Abort' + const continueButtonLabel = props.continueButtonLabel || 'Confirm' return ( @@ -25,15 +27,17 @@ export default function ConfirmationDialog(props: { onDismiss={props.onCancel} > {titleLabel} - - {props.message} - + { + props.message + ? {props.message} + : props.children + } - + - + diff --git a/src/components/styles/StyledButton.styles.ts b/src/components/styles/StyledButton.styles.ts index 53b3e45..12414c3 100644 --- a/src/components/styles/StyledButton.styles.ts +++ b/src/components/styles/StyledButton.styles.ts @@ -6,12 +6,12 @@ export const StyledButton = styled(Button)` height: 40px; width: 290px; border-radius: 20px; - justify-content: flex-end; + justify-content: center; align-items: center; font-size: 16px; font-weight: 800; ${props => props.mode === `contained` && props.disabled === false - ? `background-color: #A017B7; border-color: #A017B7; border-width: 1px; color: #ffffff;` - : `border-width: 1px; color: #A017B7; border-color: #A017B7;`}; + ? `background-color: ${props.color || '#A017B7'}; border-color: ${props.color || '#A017B7'}; border-width: 1px; color: #ffffff;` + : `border-width: 1px; color: ${props.color || '#A017B7'}; border-color: ${props.color || '#A017B7'};`}; ${props => props.disabled === true && `border-color: transparent;`}; -` \ No newline at end of file +` diff --git a/src/constants/Text.ts b/src/constants/Text.ts index d03c2fe..bd19ddb 100644 --- a/src/constants/Text.ts +++ b/src/constants/Text.ts @@ -143,6 +143,10 @@ export const PROFILE = { PHRASE: 'Backup Phrase (Seed)', DISCOVER_PRIVATEKEY: 'Show private key', DISCOVER_PHRASE: 'Show backup phrase', + DELETE_ACCOUNT: 'Delete account', + DELETE_ACCOUNT_LABEL: 'Are you sure you want to delete your account?', + DELETE_ACCOUNT_MESSAGE: 'You\'re about to delete your account. This action is irreversible and may result in a loss of funds.', + DELETE_ACCOUNT_MESSAGE_2: 'Make sure you have a backup of your seed phrase before proceeding.', } export const LOCKED_SCREEN = { diff --git a/src/navigation/index.tsx b/src/navigation/index.tsx index 9657954..ecc1884 100644 --- a/src/navigation/index.tsx +++ b/src/navigation/index.tsx @@ -130,11 +130,11 @@ function RootNavigator(): any { ) => ({ headerBackTitleVisible: false, headerTitle: 'MY ACCOUNT', headerTitleStyle: { fontSize: 20, color: '#000000' }, - }} + })} /> ) { const [isLoading, setIsLoading] = useState(true) const [accountInformation, setAccountInformation] = useState(Object.create(null)) const [isKeyBlur, setIsKeyBlur] = useState(true) const [isSeedBlur, setIsSeedBlur] = useState(true) const [accountNickname, setAccountNickname] = useState("") + const [showConfirmDelete, setShowConfirmDelete] = useState(false) const { address, publicKey, privateKey, seed } = accountInformation @@ -32,6 +36,16 @@ export default function ProfileScreen() { } }, []) + const deleteAccount = () => { + setShowConfirmDelete(false) + LTOService.deleteAccount().then(() => { + navigation.reset({ + index: 0, + routes: [{ name: 'SignUp' }], + }) + }) + } + const readStorage = () => { LTOService.getAccount() .then(account => { @@ -111,8 +125,29 @@ export default function ProfileScreen() { } + + setShowConfirmDelete(true)} + > + {PROFILE.DELETE_ACCOUNT} + } + setShowConfirmDelete(false)} + onPress={() => deleteAccount()} + > + {PROFILE.DELETE_ACCOUNT_MESSAGE} + {PROFILE.DELETE_ACCOUNT_MESSAGE_2} + ) } diff --git a/src/services/LTO.service.ts b/src/services/LTO.service.ts index eb2ed60..8a03e2a 100644 --- a/src/services/LTO.service.ts +++ b/src/services/LTO.service.ts @@ -1,6 +1,7 @@ import { Account, CancelLease, Lease, LTO, Transaction } from "@ltonetwork/lto" import LocalStorageService from "./LocalStorage.service" import { TypedTransaction } from "../interfaces/TypedTransaction" + export const lto = new LTO(process.env.LTO_NETWORK_ID) if (process.env.LTO_API_URL) lto.nodeAddress = process.env.LTO_API_URL @@ -8,7 +9,7 @@ export default class LTOService { static account?: Account public static isUnlocked = (): boolean => { - return !!LTOService.account + return !!this.account } public static unlock = async (password: string | undefined, signature?: string) => { @@ -19,44 +20,44 @@ export default class LTOService { const seed = encryptedAccount.seed[seedIndex] if (signature) { - LTOService.account = lto.account({ seedPassword: encodedSignature, seed }) + this.account = lto.account({ seedPassword: encodedSignature, seed }) } else { - LTOService.account = lto.account({ seedPassword: password, seed }) + this.account = lto.account({ seedPassword: password, seed }) } } public static lock = () => { - delete LTOService.account + delete this.account } public static getAccount = async (): Promise => { - if (!LTOService.account) { + if (!this.account) { throw new Error("Not logged in") } - return LTOService.account + return this.account } public static storeAccount = async (nickname: string, password: string, signature?: string) => { - if (!LTOService.account) { + if (!this.account) { throw new Error("Account not created") } const encodedSignature = signature && encodeURIComponent(signature) - const encryptedWithSignature = encodedSignature ? LTOService.account.encryptSeed(encodedSignature) : undefined - const encryptedWithPassword = LTOService.account.encryptSeed(password) + const encryptedWithSignature = encodedSignature ? this.account.encryptSeed(encodedSignature) : undefined + const encryptedWithPassword = this.account.encryptSeed(password) await LocalStorageService.storeData('@accountData', [{ nickname: nickname, - address: LTOService.account.address, + address: this.account.address, seed: [encryptedWithSignature, encryptedWithPassword], }]) } public static createAccount = async () => { try { - LTOService.account = lto.account() + this.account = lto.account() } catch (error) { throw new Error('Error creating account') } @@ -64,19 +65,27 @@ export default class LTOService { public static importAccount = async (seed: string) => { try { - LTOService.account = lto.account({ seed: seed }) + this.account = lto.account({ seed: seed }) } catch (error) { throw new Error('Error importing account from seeds') } } + public static deleteAccount = async () => { + await Promise.all([ + LocalStorageService.removeData('@accountData'), + LocalStorageService.removeData('@userAlias') + ]) + this.lock() + } + private static apiUrl = (path: string): string => { return lto.nodeAddress.replace(/\/$/g, '') + path } public static getBalance = async (address: string) => { try { - const url = LTOService.apiUrl(`/addresses/balance/details/${address}`) + const url = this.apiUrl(`/addresses/balance/details/${address}`) const response = await fetch(url) return response.json() } catch (error) { @@ -85,7 +94,7 @@ export default class LTOService { } public static getTransactions = async (address: string, limit?: number, page = 1) => { - const pending = await LTOService.getPendingTransactions(address) + const pending = await this.getPendingTransactions(address) let offset if (!limit) { @@ -101,12 +110,12 @@ export default class LTOService { return ([] as TypedTransaction[]).concat( pending.slice(limit * (page - 1), limit), - limit > 0 ? await LTOService.getProcessedTransactions(address, limit, offset) : [] + limit > 0 ? await this.getProcessedTransactions(address, limit, offset) : [] ) } private static getPendingTransactions = async (address: string) => { - const url = LTOService.apiUrl(`/transactions/unconfirmed`) + const url = this.apiUrl(`/transactions/unconfirmed`) const response = await fetch(url) const utx: TypedTransaction[] = await response.json() @@ -117,7 +126,7 @@ export default class LTOService { } private static getProcessedTransactions = async (address: string, limit = 100, offset = 0) => { - const url = LTOService.apiUrl(`/transactions/address/${address}?limit=${limit}&offset=${offset}`) + const url = this.apiUrl(`/transactions/address/${address}?limit=${limit}&offset=${offset}`) const response = await fetch(url) const [txs] = await response.json() @@ -126,8 +135,8 @@ export default class LTOService { public static getLeases = async (address: string) => { const [pending, active] = await Promise.all([ - LTOService.getPendingTransactions(address), - LTOService.getActiveLeases(address) + this.getPendingTransactions(address), + this.getActiveLeases(address) ]) const leases = [...pending.filter(tx => tx.type === Lease.TYPE), ...active] @@ -140,14 +149,14 @@ export default class LTOService { } private static getActiveLeases = async (address: string) => { - const url = LTOService.apiUrl(`/leasing/active/${address}`) + const url = this.apiUrl(`/leasing/active/${address}`) const response = await fetch(url) return response.json() } public static broadcast = async (transaction: Transaction) => { - const url = LTOService.apiUrl('/transactions/broadcast') + const url = this.apiUrl('/transactions/broadcast') const response = await fetch(url, { method: 'POST', headers: {