From 18ea9873e15979e60f729230e18c4e03f9f74bba Mon Sep 17 00:00:00 2001 From: Bartosz Date: Thu, 5 Oct 2023 11:48:35 +0200 Subject: [PATCH] refactor: move invites vuex state to composable --- src/composables/index.ts | 1 + src/composables/invites.ts | 93 +++++++++++++++++++ src/constants/common.ts | 1 + .../006-invites-vuex-to-composable.ts | 14 +++ src/popup/components/InviteItem.vue | 18 ++-- src/popup/components/Modals/ClaimGiftCard.vue | 6 +- src/popup/pages/Invite.vue | 17 ++-- src/popup/pages/InviteClaim.vue | 7 +- src/store/index.js | 2 - src/store/modules/invites.js | 62 ------------- src/types/index.ts | 5 + 11 files changed, 140 insertions(+), 86 deletions(-) create mode 100644 src/composables/invites.ts create mode 100644 src/migrations/006-invites-vuex-to-composable.ts delete mode 100644 src/store/modules/invites.js diff --git a/src/composables/index.ts b/src/composables/index.ts index 5443acb3f..43d3cd62a 100644 --- a/src/composables/index.ts +++ b/src/composables/index.ts @@ -4,6 +4,7 @@ export * from './copy'; export * from './connection'; export * from './currencies'; export * from './deepLinkApi'; +export * from './invites'; export * from './latestTransactionList'; export * from './maxAmount'; export * from './middleware'; diff --git a/src/composables/invites.ts b/src/composables/invites.ts new file mode 100644 index 000000000..793b1e3ac --- /dev/null +++ b/src/composables/invites.ts @@ -0,0 +1,93 @@ +import { + AE_AMOUNT_FORMATS, + AeSdk, + Encoded, + Node, +} from '@aeternity/aepp-sdk'; +import type { IInvite } from '@/types'; +import { STORAGE_KEYS } from '@/constants'; +import { tg } from '@/popup/plugins/i18n'; +import { getAccountFromSecret } from '@/protocols/aeternity/helpers'; +import migrateInvitesVuexToComposable from '@/migrations/006-invites-vuex-to-composable'; +import { useStorageRef } from './storageRef'; +import { useModals } from './modals'; +import { useNetworks } from './networks'; + +const invites = useStorageRef( + [], + STORAGE_KEYS.invites, + { + migrations: [ + migrateInvitesVuexToComposable, + ], + }, +); + +export function useInvites() { + const { activeNetwork } = useNetworks(); + const { openDefaultModal } = useModals(); + + function addInvite(secretKey: Buffer) { + invites.value.unshift({ + secretKey: secretKey.toJSON(), + createdAt: Date.now(), + }); + } + + function removeInvite(secretKey: Buffer) { + invites.value = invites.value.filter((invite) => invite.secretKey !== secretKey); + } + + async function claimInvite({ + secretKey, + recipientId, + amount = '0', + isMax = false, + }: { secretKey: Buffer; recipientId: Encoded.AccountAddress; amount?: string; isMax: boolean }) { + const aeSdk = new AeSdk({ + nodes: [{ + name: activeNetwork.value.name, + instance: new Node(activeNetwork.value.protocols.aeternity.nodeUrl), + }], + accounts: [getAccountFromSecret(secretKey)], + }); + if (!isMax) { + await aeSdk.spend( + amount, + recipientId, + // @ts-ignore + { denomination: AE_AMOUNT_FORMATS.AE }, + ); + } else { + await aeSdk.transferFunds( + 1, // Decimal percentage, 1 = 100% + recipientId, + { verify: false }, + ); + } + } + + async function handleInsufficientBalanceError(error: Error, isInviteError = false) { + if ( + (!isInviteError && !error.message.includes('is not enough to execute')) + || (isInviteError && !error.message.includes('Transaction build error')) + ) { + return false; + } + + await openDefaultModal({ + msg: (isInviteError) + ? tg('pages.invite.insufficient-invite-balance') + : tg('pages.invite.insufficient-balance'), + }); + return true; + } + + return { + invites, + addInvite, + removeInvite, + claimInvite, + handleInsufficientBalanceError, + }; +} diff --git a/src/constants/common.ts b/src/constants/common.ts index 04f594100..d1911cac4 100644 --- a/src/constants/common.ts +++ b/src/constants/common.ts @@ -147,6 +147,7 @@ export const STORAGE_KEYS = { hiddenCards: 'hidden-cards', otherSettings: 'other-settings', errorLog: 'error-log', + invites: 'invites', } as const; export const CURRENCIES: ICurrency[] = [ diff --git a/src/migrations/006-invites-vuex-to-composable.ts b/src/migrations/006-invites-vuex-to-composable.ts new file mode 100644 index 000000000..e0da500f5 --- /dev/null +++ b/src/migrations/006-invites-vuex-to-composable.ts @@ -0,0 +1,14 @@ +import type { IInvite, Migration } from '@/types'; +import { collectVuexState } from './migrationHelpers'; + +const migration: Migration = async (restoredValue: IInvite[]) => { + if (!restoredValue?.length) { + const invites = (await collectVuexState())?.invites?.invites; + if (invites?.length) { + return invites; + } + } + return restoredValue; +}; + +export default migration; diff --git a/src/popup/components/InviteItem.vue b/src/popup/components/InviteItem.vue index 1fe48ef04..e2db707c8 100644 --- a/src/popup/components/InviteItem.vue +++ b/src/popup/components/InviteItem.vue @@ -102,10 +102,11 @@ import { import { ROUTE_INVITE_CLAIM } from '@/popup/router/routeNames'; import { useAccounts, - useBalances, - useMaxAmount, useAeSdk, + useBalances, useCurrencies, + useInvites, + useMaxAmount, } from '@/composables'; import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory'; @@ -134,6 +135,7 @@ export default defineComponent({ const { marketData } = useCurrencies(); const { balance } = useBalances(); const { getAeSdk } = useAeSdk({ store }); + const { claimInvite, removeInvite, handleInsufficientBalanceError } = useInvites(); const formModel = ref({ amount: '', @@ -160,7 +162,7 @@ export default defineComponent({ const address = computed(() => getAccountFromSecret(props.secretKey).address); function deleteItem() { - store.commit('invites/delete', props.secretKey); + removeInvite(props.secretKey); } async function updateBalance() { @@ -177,14 +179,14 @@ export default defineComponent({ async function claim() { emit('loading', true); try { - await store.dispatch('invites/claim', { + await claimInvite({ secretKey: Buffer.from(props.secretKey), - recipientId: getLastActiveProtocolAccount(PROTOCOL_AETERNITY)?.address, + recipientId: getLastActiveProtocolAccount(PROTOCOL_AETERNITY)?.address!, isMax: true, }); await updateBalance(); - } catch (error) { - if (await store.dispatch('invites/handleNotEnoughFoundsError', { error, isInviteError: true })) { + } catch (error: any) { + if (await handleInsufficientBalanceError(error, true)) { return; } throw error; @@ -215,7 +217,7 @@ export default defineComponent({ await updateBalance(); resetTopUpChanges(); } catch (error: any) { - if (await store.dispatch('invites/handleNotEnoughFoundsError', { error })) { + if (await handleInsufficientBalanceError(error)) { return; } throw error; diff --git a/src/popup/components/Modals/ClaimGiftCard.vue b/src/popup/components/Modals/ClaimGiftCard.vue index 8712b922f..ae710b529 100644 --- a/src/popup/components/Modals/ClaimGiftCard.vue +++ b/src/popup/components/Modals/ClaimGiftCard.vue @@ -120,6 +120,7 @@ import { useAccounts, useAeSdk, useCurrencies, + useInvites, } from '@/composables'; import { AE_COIN_PRECISION, AE_SYMBOL } from '@/protocols/aeternity/config'; import { getAccountFromSecret } from '@/protocols/aeternity/helpers'; @@ -167,6 +168,7 @@ export default defineComponent({ const { getAeSdk } = useAeSdk({ store }); const { aeAccounts, aeAccountsSelectOptions } = useAccounts(); const { getFormattedFiat } = useCurrencies(); + const { claimInvite } = useInvites(); const recipientId = ref(aeAccounts.value[0].address); const amount = ref(''); @@ -226,7 +228,7 @@ export default defineComponent({ switch (step.value) { case STEPS.initial: setMaxAmount(); - await store.dispatch('invites/claim', { + await claimInvite({ secretKey: props.secretKey, recipientId: recipientId.value, isMax: true, @@ -234,7 +236,7 @@ export default defineComponent({ step.value = STEPS.redeemFull; break; case STEPS.form: - await store.dispatch('invites/claim', { + await claimInvite({ secretKey: props.secretKey, recipientId: recipientId.value, amount: amount.value, diff --git a/src/popup/pages/Invite.vue b/src/popup/pages/Invite.vue index 2abfff957..87754ed0c 100644 --- a/src/popup/pages/Invite.vue +++ b/src/popup/pages/Invite.vue @@ -76,14 +76,14 @@ import { useStore } from 'vuex'; import type { IFormModel } from '@/types'; import { ProtocolAdapterFactory } from '@/lib/ProtocolAdapterFactory'; import { PROTOCOL_AETERNITY } from '@/constants'; -import { useState } from '../../composables/vuex'; import { useAccounts, useAeSdk, useBalances, - useMaxAmount, useCurrencies, -} from '../../composables'; + useInvites, + useMaxAmount, +} from '@/composables'; import AccountInfo from '../components/AccountInfo.vue'; import BalanceInfo from '../components/BalanceInfo.vue'; @@ -111,6 +111,7 @@ export default defineComponent({ const { marketData } = useCurrencies(); const { getAeSdk } = useAeSdk({ store }); const { balance } = useBalances(); + const { invites, addInvite, handleInsufficientBalanceError } = useInvites(); const formModel = ref({ amount: '', @@ -121,8 +122,6 @@ export default defineComponent({ const { max, fee } = useMaxAmount({ formModel, store }); - const invites = useState('invites', 'invites'); - async function generate() { loading.value = true; const { publicKey, secretKey } = generateKeyPair(); @@ -135,14 +134,16 @@ export default defineComponent({ // @ts-ignore { denomination: AE_AMOUNT_FORMATS.AE }, ); - } catch (error) { - if (await store.dispatch('invites/handleNotEnoughFoundsError', { error })) return; + } catch (error: any) { + if (await handleInsufficientBalanceError(error)) { + return; + } throw error; } finally { loading.value = false; } - store.commit('invites/add', Buffer.from(secretKey, 'hex').slice(0, 32)); + addInvite(Buffer.from(secretKey, 'hex').slice(0, 32)); formModel.value.amount = ''; } diff --git a/src/popup/pages/InviteClaim.vue b/src/popup/pages/InviteClaim.vue index ea03abe0a..5d54c0d28 100644 --- a/src/popup/pages/InviteClaim.vue +++ b/src/popup/pages/InviteClaim.vue @@ -1,19 +1,18 @@