From 7c7c1daadd1bfe01f6dffc668278c35653cd1b0c Mon Sep 17 00:00:00 2001 From: Duddino Date: Sat, 25 Nov 2023 18:05:33 +0100 Subject: [PATCH] Refactor Dashboard with useWallet and useSettings composables (#271) * Refactor Dashboard with useWallet and useSettings composables * Review changes --- scripts/composables/use_settings.js | 19 +++++ scripts/composables/use_wallet.js | 74 ++++++++++++++++++++ scripts/dashboard/Dashboard.vue | 104 +++++++++++++--------------- scripts/wallet.js | 6 +- 4 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 scripts/composables/use_settings.js create mode 100644 scripts/composables/use_wallet.js diff --git a/scripts/composables/use_settings.js b/scripts/composables/use_settings.js new file mode 100644 index 000000000..306e8b0ff --- /dev/null +++ b/scripts/composables/use_settings.js @@ -0,0 +1,19 @@ +import { getEventEmitter } from '../event_bus.js'; +import { ref } from 'vue'; +import { nDisplayDecimals, fAdvancedMode } from '../settings.js'; + +export function useSettings() { + const advancedMode = ref(fAdvancedMode); + const displayDecimals = ref(0); + + getEventEmitter().on('advanced-mode', (fAdvancedMode) => { + advancedMode.value = fAdvancedMode; + }); + getEventEmitter().on('balance-update', async () => { + displayDecimals.value = nDisplayDecimals; + }); + return { + advancedMode, + displayDecimals, + }; +} diff --git a/scripts/composables/use_wallet.js b/scripts/composables/use_wallet.js new file mode 100644 index 000000000..bf6012bd9 --- /dev/null +++ b/scripts/composables/use_wallet.js @@ -0,0 +1,74 @@ +import { getEventEmitter } from '../event_bus.js'; +import { hasEncryptedWallet, wallet } from '../wallet.js'; +import { ref } from 'vue'; +import { strCurrency } from '../settings.js'; +import { mempool } from '../global.js'; +import { cMarket } from '../settings.js'; + +/** + * This is the middle ground between vue and the wallet class + * It makes sure that everything is up to date and provides + * a reactive interface to it + */ +export function useWallet() { + // Eventually we want to create a new wallet + // For now we'll just import the existing one + // const wallet = new Wallet(); + + const isImported = ref(wallet.isLoaded()); + const isViewOnly = ref(wallet.isViewOnly()); + const getKeyToBackup = async () => await wallet.getKeyToBackup(); + const isEncrypted = ref(true); + + const setMasterKey = (mk) => { + wallet.setMasterKey(mk); + isImported.value = wallet.isLoaded(); + isHardwareWallet.value = wallet.isHardwareWallet(); + isHD.value = wallet.isHD(); + isViewOnly.value = wallet.isViewOnly(); + hasEncryptedWallet().then((i) => (isEncrypted.value = i)); + }; + const getAddress = () => wallet.getAddress(); + const isHardwareWallet = ref(wallet.isHardwareWallet()); + const isHD = ref(wallet.isHD()); + const checkDecryptPassword = async (passwd) => + await wallet.checkDecryptPassword(passwd); + + hasEncryptedWallet().then((r) => { + isEncrypted.value = r; + }); + + const encrypt = async (passwd) => { + await wallet.encrypt(passwd); + isEncrypted.value = await hasEncryptedWallet(); + }; + const balance = ref(0); + const currency = ref('USD'); + const price = ref(0.0); + + getEventEmitter().on('balance-update', async () => { + balance.value = mempool.balance; + currency.value = strCurrency.toUpperCase(); + price.value = await cMarket.getPrice(strCurrency); + }); + + return { + isImported, + isViewOnly, + isEncrypted, + getKeyToBackup, + setMasterKey, + isHardwareWallet, + checkDecryptPassword, + encrypt, + getAddress, + wipePrivateData: () => { + wallet.wipePrivateData(); + isViewOnly.value = wallet.isViewOnly(); + }, + isHD, + balance, + currency, + price, + }; +} diff --git a/scripts/dashboard/Dashboard.vue b/scripts/dashboard/Dashboard.vue index 78e22ed2a..b40709247 100644 --- a/scripts/dashboard/Dashboard.vue +++ b/scripts/dashboard/Dashboard.vue @@ -5,11 +5,7 @@ import Activity from './Activity.vue'; import GenKeyWarning from './GenKeyWarning.vue'; import TransferMenu from './TransferMenu.vue'; import ExportPrivKey from './ExportPrivKey.vue'; -import { - cleanAndVerifySeedPhrase, - hasEncryptedWallet, - wallet, -} from '../wallet.js'; +import { cleanAndVerifySeedPhrase } from '../wallet.js'; import { parseWIF, verifyWIF } from '../encoding.js'; import { createAlert, @@ -26,7 +22,7 @@ import { } from '../masterkey'; import { decrypt } from '../aes-gcm.js'; import { cChainParams, COIN } from '../chain_params'; -import { onMounted, ref, watch } from 'vue'; +import { onMounted, ref, watch, computed } from 'vue'; import { mnemonicToSeed } from 'bip39'; import { getEventEmitter } from '../event_bus'; import { Database } from '../database'; @@ -36,7 +32,6 @@ import { updateEncryptionGUI, updateLogOutButton, } from '../global'; -import { cMarket, nDisplayDecimals, strCurrency } from '../settings.js'; import { mempool, refreshChainData } from '../global.js'; import { confirmPopup, @@ -49,16 +44,22 @@ import { validateAmount, createAndSendTransaction } from '../transactions.js'; import { strHardwareName } from '../ledger'; import { guiAddContactPrompt } from '../contacts-book'; import { scanQRCode } from '../scanner'; +import { useWallet } from '../composables/use_wallet.js'; +import { useSettings } from '../composables/use_settings.js'; -const isImported = ref(wallet.isLoaded()); -const isViewOnly = ref(wallet.isViewOnly()); +const wallet = useWallet(); const activity = ref(null); -const needsToEncrypt = ref(true); +const needsToEncrypt = computed(() => { + if (wallet.isHardwareWallet.value) { + return false; + } else { + return !wallet.isViewOnly.value && !wallet.isEncrypted.value; + } +}); const showTransferMenu = ref(false); -const advancedMode = ref(false); +const { advancedMode, displayDecimals } = useSettings(); const showExportModal = ref(false); const showEncryptModal = ref(false); -const isEncrypt = ref(false); const keyToBackup = ref(''); const jdenticonValue = ref(''); const transferAddress = ref(''); @@ -71,9 +72,6 @@ watch(showExportModal, async (showExportModal) => { keyToBackup.value = ''; } }); -getEventEmitter().on('advanced-mode', (fAdvancedMode) => { - advancedMode.value = fAdvancedMode; -}); /** * Parses whatever the secret is to a MasterKey @@ -173,17 +171,9 @@ async function importWallet({ type, secret, password = '' }) { } if (key) { wallet.setMasterKey(key); - isImported.value = true; jdenticonValue.value = wallet.getAddress(); - isEncrypt.value = await hasEncryptedWallet(); - if (!wallet.isHardwareWallet()) { - needsToEncrypt.value = !wallet.isViewOnly() && !isEncrypt.value; - } else { - needsToEncrypt.value = false; - } if (needsToEncrypt.value) showEncryptModal.value = true; - isViewOnly.value = wallet.isViewOnly(); await mempool.loadFromDisk(); getNetwork().walletFullSync(); getEventEmitter().emit('wallet-import'); @@ -199,7 +189,7 @@ async function importWallet({ type, secret, password = '' }) { * @param {string} [currentPassword] - Current password with which the wallet is encrypted with, if any */ async function encryptWallet(password, currentPassword = '') { - if (await hasEncryptedWallet()) { + if (wallet.isEncrypted.value) { if (!(await wallet.checkDecryptPassword(currentPassword))) { createAlert('warning', ALERTS.INCORRECT_PASSWORD, 6000); return false; @@ -209,15 +199,13 @@ async function encryptWallet(password, currentPassword = '') { if (res) { createAlert('success', ALERTS.NEW_PASSWORD_SUCCESS, 5500); } - needsToEncrypt.value = false; - isEncrypt.value = await hasEncryptedWallet(); // TODO: refactor once settings is written await updateEncryptionGUI(); } // TODO: This needs to be vueeifed a bit async function restoreWallet(strReason) { - if (wallet.isHardwareWallet()) return true; + if (wallet.isHardwareWallet.value) return true; // Build up the UI elements based upon conditions for the unlock prompt let strHTML = ''; @@ -241,7 +229,6 @@ async function restoreWallet(strReason) { const key = await parseSecret(encWif, strPassword); if (key) { wallet.setMasterKey(key); - isViewOnly.value = wallet.isViewOnly(); createAlert('success', ALERTS.WALLET_UNLOCKED, 1500); return true; } else { @@ -258,7 +245,7 @@ async function restoreWallet(strReason) { * Lock the wallet by deleting masterkey private data */ async function lockWallet() { - const isEncrypted = await hasEncryptedWallet(); + const isEncrypted = wallet.isEncrypted.value; const title = isEncrypted ? translation.popupWalletLock : translation.popupWalletWipe; @@ -272,7 +259,6 @@ async function lockWallet() { }) ) { wallet.wipePrivateData(); - isViewOnly.value = wallet.isViewOnly(); createAlert('success', ALERTS.WALLET_LOCKED, 1500); } } @@ -284,11 +270,23 @@ async function lockWallet() { */ async function send(address, amount) { // Ensure a wallet is loaded - if (!(await wallet.hasWalletUnlocked(true))) return; + if (wallet.isViewOnly.value) { + return createAlert( + 'warning', + tr(ALERTS.WALLET_UNLOCK_IMPORT, [ + { + unlock: wallet.isEncrypted.value + ? 'unlock ' + : 'import/create', + }, + ]), + 3500 + ); + } // Ensure the wallet is unlocked if ( - wallet.isViewOnly() && + wallet.isViewOnly.value && !(await restoreWallet(translation.walletUnlockTx)) ) return; @@ -365,17 +363,15 @@ async function send(address, amount) { getEventEmitter().on('toggle-network', async () => { const database = await Database.getInstance(); const account = await database.getAccount(); - wallet.reset(); wallet.setMasterKey(null); activity.value?.reset(); if (account) { await importWallet({ type: 'hd', secret: account.publicKey }); } else { - isImported.value = false; await (await Database.getInstance()).removeAllTxs(); } - await updateEncryptionGUI(wallet.isLoaded()); + await updateEncryptionGUI(wallet.isImported.value); updateLogOutButton(); // TODO: When tab component is written, simply emit an event doms.domDashboard.click(); @@ -384,7 +380,7 @@ getEventEmitter().on('toggle-network', async () => { onMounted(async () => { await start(); - if (await hasEncryptedWallet()) { + if (wallet.isEncrypted.value) { const database = await Database.getInstance(); const { publicKey } = await database.getAccount(); await importWallet({ type: 'hd', secret: publicKey }); @@ -405,26 +401,16 @@ onMounted(async () => { updateLogOutButton(); }); -const balance = ref(0); -const currency = ref('USD'); -const price = ref(0.0); -const displayDecimals = ref(0); +const { balance, currency, price } = wallet; getEventEmitter().on('sync-status', (status) => { if (status === 'stop') activity?.value?.update(); }); -getEventEmitter().on('new-tx', (status) => { +getEventEmitter().on('new-tx', () => { activity?.value?.update(); }); -getEventEmitter().on('balance-update', async () => { - balance.value = mempool.balance; - currency.value = strCurrency.toUpperCase(); - price.value = await cMarket.getPrice(strCurrency); - displayDecimals.value = nDisplayDecimals; -}); - function changePassword() { showEncryptModal.value = true; } @@ -485,7 +471,7 @@ defineExpose({
@@ -495,7 +481,11 @@ defineExpose({
@@ -881,21 +875,21 @@ defineExpose({ @close="showExportModal = false" /> -
+
} If the wallet has an encrypted database backup + * @returns {Promise} If the wallet has an encrypted database backup */ export async function hasEncryptedWallet() { const database = await Database.getInstance();