Skip to content

Commit

Permalink
Add clear history button (#546)
Browse files Browse the repository at this point in the history
* Add clear history button to AccountSent page
  • Loading branch information
alexkeating committed Jul 7, 2023
1 parent 4797a1b commit 9149d46
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 45 deletions.
56 changes: 48 additions & 8 deletions frontend/src/components/AccountSentTable.vue
Original file line number Diff line number Diff line change
@@ -1,13 +1,47 @@
<template>
<div>
<div class="text-caption q-mb-sm">
{{ $t('AccountSentTable.stored-on-device') }}.
<router-link
class="cursor-pointer hyperlink"
:to="{ name: 'FAQ', hash: '#why-cant-I-see-my-send-history-on-different-devices' }"
>{{ $t('AccountSentTable.learn-more') }}</router-link
>.
<div class="flex row justify-between q-mb-sm">
<div class="text-caption self-end">
{{ $t('AccountSentTable.stored-on-device') }}.
<router-link
class="cursor-pointer hyperlink"
:to="{ name: 'FAQ', hash: '#why-cant-I-see-my-send-history-on-different-devices' }"
>{{ $t('AccountSentTable.learn-more') }}</router-link
>.
</div>
<div class="flex row items-center" v-if="formattedSendMetadata.length > 0">
<base-button
size="sm"
@click="showClearHistoryWarning = true"
icon="fa fa-trash"
:flat="true"
:label="$t('AccountSent.clear-history')"
/>
</div>
</div>
<q-dialog v-model="showClearHistoryWarning">
<q-card class="row justify-center q-my-none q-py-none border-top-thick">
<q-card-section>
<h5 class="text-bold text-center q-mt-none">
<q-icon name="fas fa-exclamation-triangle" color="warning" left /> {{ $t('Utils.Dialog.warning') }}
</h5>
</q-card-section>
<q-card-section>
<div v-html="$t('AccountSentTable.clear-history-warning')" />
</q-card-section>
<q-card-section class="q-pt-sm">
<div class="row justify-evenly">
<base-button
class="q-mr-sm"
:outline="true"
@click="showClearHistoryWarning = false"
:label="$t('AccountSentTable.cancel')"
/>
<base-button type="submit" @click="clearHistory()" :label="$t('AccountSentTable.clear-history')" />
</div>
</q-card-section>
</q-card>
</q-dialog>
<q-table
:grid="$q.screen.xs"
card-container-class="col q-col-gutter-md"
Expand Down Expand Up @@ -123,7 +157,7 @@
</template>

<script lang="ts">
import { defineComponent, PropType } from 'vue';
import { defineComponent, PropType, ref } from 'vue';
import { SendTableMetadataRow } from 'components/models';
import BaseTooltip from 'src/components/BaseTooltip.vue';
import useWalletStore from 'src/store/wallet';
Expand All @@ -138,10 +172,15 @@ export default defineComponent({
type: undefined as unknown as PropType<SendTableMetadataRow[]>,
required: true,
},
clearHistory: {
type: Function,
required: true,
},
},
setup(props, context) {
const { provider, chainId } = useWalletStore();
const paginationConfig = { rowsPerPage: 25 };
const showClearHistoryWarning = ref(false);
const mainTableColumns = [
{
align: 'left',
Expand Down Expand Up @@ -174,6 +213,7 @@ export default defineComponent({
openInEtherscan,
provider,
chainId,
showClearHistoryWarning,
};
},
});
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@
"account-empty": "No funds sent in this browser",
"advanced-mode-on": "Sent with advanced mode enabled",
"amount": "Amount",
"cancel": "Cancel",
"clear-history": "Clear History",
"clear-history-warning": "This action cannot be undone! Your encrypted send history is only stored locally on the device that <b>sent the transaction and cannot be recovered once cleared</b>. Are you sure you want to continue?",
"date-sent": "Date sent",
"learn-more": "Learn more",
"receiver": "Receiver",
Expand All @@ -321,6 +324,7 @@
"use-public-key-checked": "Sent using recipient's standard public key"
},
"AccountSent": {
"clear-history": "Clear history",
"connect-wallet": "Connect Wallet",
"connect-your-wallet": "Connect your wallet to view sent funds",
"fetching-send-history": "Fetching send history...",
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/i18n/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,9 @@
"account-empty": "此浏览器中未发送任何资金",
"advanced-mode-on": "有启用高级模式发送",
"amount": "金额",
"cancel": "取消",
"clear-history": "清除历史记录",
"clear-history-warning": "此操作无法撤消!您的加密发送历史记录仅存储在发送交易的本地设备上,一旦清除就无法恢复。您确定要继续吗?",
"date-sent": "发送日期",
"learn-more": "了解更多信息",
"receiver": "收款人",
Expand All @@ -322,6 +325,7 @@
},

"AccountSent": {
"clear-history": "清除历史记录",
"connect-wallet": "连接钱包",
"connect-your-wallet": "连接您的钱包来查看已发送的资金",
"fetching-send-history": "正在获取发送历史记录...",
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/pages/AccountSent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@
<loading-spinner />
<div class="text-center text-italic">{{ $t('AccountSent.fetching-send-history') }}</div>
</div>
<div v-else-if="!needsSignature && !dataLoading" class="q-mx-auto" style="max-width: 800px">
<account-sent-table :sendMetadata="sendMetadata" />
<div v-else-if="!needsSignature && !dataLoading" class="q-mx-auto flex column" style="max-width: 800px">
<account-sent-table :sendMetadata="sendMetadata" :clearHistory="clearHistory" />
</div>
</div>
</q-page>
Expand All @@ -34,7 +34,7 @@ import { SendTableMetadataRow } from 'components/models';
import { BigNumber } from 'src/utils/ethers';
import { formatNameOrAddress } from 'src/utils/address';
import { formatDate, formatAmount, formatTime, getTokenSymbol, getTokenLogoUri } from 'src/utils/utils';
import { fetchAccountSends } from 'src/utils/account-send';
import { clearAccountSend, fetchAccountSends } from 'src/utils/account-send';
function useAccountSent() {
const { tokens, userAddress, chainId, viewingKeyPair, getPrivateKeys } = useWalletStore();
Expand Down Expand Up @@ -79,6 +79,13 @@ function useAccountSent() {
dataLoading.value = false;
};
const clearHistory = async () => {
dataLoading.value = true;
await clearAccountSend(userAddress.value!, chainId.value!);
sendMetadata.value = [];
dataLoading.value = false;
};
onMounted(async () => {
if (!needsSignature.value) {
await getData();
Expand All @@ -89,6 +96,7 @@ function useAccountSent() {
userAddress,
sendMetadata,
getData,
clearHistory,
viewingPrivateKey,
needsSignature,
dataLoading,
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/utils/account-send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,9 @@ export const fetchAccountSends = async ({ address, viewingPrivateKey, chainId }:
});
return accountData.reverse();
};

export const clearAccountSend = async (address: string, chainId: number) => {
const localStorageKey = `${LOCALFORAGE_ACCOUNT_SEND_KEY_PREFIX}-${address}-${chainId}`;
const localStorageCountKey = `${LOCALFORAGE_ACCOUNT_SEND_KEY_PREFIX}-count-${address}-${chainId}`;
await Promise.all([localforage.removeItem(localStorageKey), localforage.removeItem(localStorageCountKey)]);
};
113 changes: 79 additions & 34 deletions frontend/test/account-send.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RandomNumber } from '@umbracash/umbra-js';

import {
buildAccountDataForEncryption,
clearAccountSend,
decryptData,
encryptAccountData,
fetchAccountSends,
Expand All @@ -24,6 +25,23 @@ jest.mock('src/utils/constants', () => ({
...jest.requireActual('src/utils/constants'),
MAINNET_PROVIDER: jest.fn(),
}));
window.logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
version: '',
_log: jest.fn(),
makeError: jest.fn(),
assert: jest.fn(),
assertArgument: jest.fn(),
checkNormalize: jest.fn(),
checkArgumentCount: jest.fn(),
checkNew: jest.fn(),
checkAbstract: jest.fn(),
checkSafeUint53: jest.fn(),
throwError: jest.fn() as never,
throwArgumentError: jest.fn() as never,
};

const NUM_RUNS = 100;

Expand Down Expand Up @@ -604,23 +622,6 @@ describe('storeSend', () => {
describe('fetchAccountSends', () => {
beforeEach(async () => {
await localforage.clear();
window.logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
version: '',
_log: jest.fn(),
makeError: jest.fn(),
assert: jest.fn(),
assertArgument: jest.fn(),
checkNormalize: jest.fn(),
checkArgumentCount: jest.fn(),
checkNew: jest.fn(),
checkAbstract: jest.fn(),
checkSafeUint53: jest.fn(),
throwError: jest.fn() as never,
throwArgumentError: jest.fn() as never,
};
});

it('Correctly fetch send data when there is a single send', async () => {
Expand Down Expand Up @@ -676,23 +677,6 @@ describe('fetchAccountSends', () => {
describe('End to end account tests', () => {
beforeEach(async () => {
await localforage.clear();
window.logger = {
debug: jest.fn(),
info: jest.fn(),
warn: jest.fn(),
version: '',
_log: jest.fn(),
makeError: jest.fn(),
assert: jest.fn(),
assertArgument: jest.fn(),
checkNormalize: jest.fn(),
checkArgumentCount: jest.fn(),
checkNew: jest.fn(),
checkAbstract: jest.fn(),
checkSafeUint53: jest.fn(),
throwError: jest.fn() as never,
throwArgumentError: jest.fn() as never,
};
});

it.each([randomInt(2, 10), randomInt(2, 10), randomInt(2, 10), randomInt(2, 10)])(
Expand Down Expand Up @@ -781,3 +765,64 @@ describe('End to end account tests', () => {
20000
);
});

describe('clearHistory', () => {
beforeEach(async () => {
await localforage.clear();
});

it.each([
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
randomInt(0, 50),
])(
"Clear account send history '%s'",
async (num) => {
const sends = createAccountSend(num);

// Offset the account sends based on when local storage was cleared
for (const [, value] of sends.accountSends.entries()) {
const {
amount,
tokenAddress,
txHash,
recipientAddress: randomRecipientAddress,
advancedMode,
usePublicKeyChecked,
pubKey,
} = value;

const storeSendArgs = {
unencryptedAccountSendData: {
amount: amount,
tokenAddress,
txHash,
// Sender address needs to be static because it is part of the key used
// to fetch data from localforage.
senderAddress: recipientAddress,
},
accountDataToEncrypt: {
recipientAddress: randomRecipientAddress,
advancedMode,
usePublicKeyChecked,
pubKey,
},
};

await storeSend(5, viewingPrivateKey, storeSendArgs);
}
await clearAccountSend(recipientAddress, 5);

const existingCount = await localforage.getItem(localStorageCountKey);
const value = await localforage.getItem(localStorageValueKey);
expect(existingCount).toEqual(null);
expect(value).toEqual(null);
},
60000
);
});

0 comments on commit 9149d46

Please sign in to comment.