Skip to content

Commit

Permalink
refactor: changed QRCodeModal to be normal component instead of modal
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonioVentilii committed May 28, 2024
1 parent 25e3383 commit f35a218
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 86 deletions.
84 changes: 0 additions & 84 deletions src/frontend/src/lib/components/send/QRCodeModal.svelte

This file was deleted.

96 changes: 96 additions & 0 deletions src/frontend/src/lib/components/send/QRCodeScan.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<script lang="ts">
import { QRCodeReader } from '@dfinity/gix-components';
import { toastsError } from '$lib/stores/toasts.store';
import { decodeQrCode } from '$lib/utils/qr-code.utils';
import { nonNullish } from '@dfinity/utils';
import type { Token } from '@dfinity/utils';
import type { QrStatus } from '$lib/types/qr-code';
import { i18n } from '$lib/stores/i18n.store';
import { createEventDispatcher, onDestroy, onMount } from 'svelte';
import ButtonGroup from '$lib/components/ui/ButtonGroup.svelte';
export let expectedToken: Token;
export let destination: string | undefined;
export let amount: number | undefined;
const dispatch = createEventDispatcher();
let timeoutId: NodeJS.Timeout | undefined;
let resolveQrCodePromise:
| (({ status, code }: { status: QrStatus; code?: string }) => void)
| undefined = undefined;
onMount(async () => {
await scanQrCode();
});
onDestroy(() => {
if (nonNullish(timeoutId)) {
clearTimeout(timeoutId);
}
});
const scanQrCode = async () => {
const result = await new Promise<{ status: QrStatus; code?: string | undefined }>((resolve) => {
resolveQrCodePromise = resolve;
timeoutId = setTimeout(() => {
resolve({ status: 'timeout' });
}, 30000);
resolveQrCodePromise = ({ status, code }) => {
clearTimeout(timeoutId);
resolve({ status, code });
};
});
const { status, code } = result;
if (status === 'timeout') {
toastsError({ msg: { text: $i18n.send.error.qr_scan_timeout } });
back();
return;
}
const qrResponse = decodeQrCode({ status, code, expectedToken });
if (qrResponse.status === 'token_incompatible') {
toastsError({ msg: { text: $i18n.send.error.incompatible_token } });
back();
return;
}
if (nonNullish(qrResponse.destination)) {
destination = qrResponse.destination;
}
if (nonNullish(qrResponse.amount)) {
amount = qrResponse.amount;
}
back();
};
const onQRCode = ({ detail: code }: CustomEvent<string>) => {
resolveQrCodePromise?.({ status: 'success', code });
resolveQrCodePromise = undefined;
};
const onCancel = () => {
resolveQrCodePromise?.({ status: 'cancelled' });
resolveQrCodePromise = undefined;
};
const back = () => {
dispatch('icBack');
};
</script>

<div class="stretch">
<QRCodeReader on:nnsCancel={onCancel} on:nnsQRCode={onQRCode} />
</div>

<ButtonGroup>
<button class="secondary block flex-1" on:click={back}>
{$i18n.core.text.back}
</button>
</ButtonGroup>
3 changes: 2 additions & 1 deletion src/frontend/src/lib/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,8 @@
"data_undefined": "Transaction Data cannot be undefined or null.",
"no_identity_calculate_fee": "No identity provided to calculate the fee for its principal.",
"invalid_destination": "The destination is invalid. Please try again with a valid wallet address or destination.",
"incompatible_token": "The token is incompatible. Please try again with a compatible token."
"incompatible_token": "The token is incompatible. Please try again with a compatible token.",
"qr_scan_timeout": "No QR code found. Please try again."
}
},
"convert": {
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/lib/types/i18n.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ interface I18nSend {
no_identity_calculate_fee: string;
invalid_destination: string;
incompatible_token: string;
qr_scan_timeout: string;
};
}

Expand Down
2 changes: 1 addition & 1 deletion src/frontend/src/lib/types/qr-code.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export type QrStatus = 'success' | 'cancelled' | 'token_incompatible';
export type QrStatus = 'success' | 'cancelled' | 'token_incompatible' | 'timeout';

export type QrResponse = {
status: QrStatus;
Expand Down
32 changes: 32 additions & 0 deletions src/frontend/src/tests/lib/components/send/QRCodeScan.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { ICP_TOKEN } from '$env/tokens.env';
import QRCodeScan from '$lib/components/send/QRCodeScan.svelte';
import { toastsError } from '$lib/stores/toasts.store';
import { render, waitFor } from '@testing-library/svelte';
import { afterEach, describe, expect, it, vi } from 'vitest';
import en from '../../../mocks/i18n.mock';

const expectedToken = ICP_TOKEN;

vi.mock('$lib/stores/toasts.store');

describe('QRCodeReaderComponent', () => {
afterEach(() => {
vi.clearAllMocks();
});

it('should show timeout error on QR scan timeout', async () => {
vi.useFakeTimers();

render(QRCodeScan, { expectedToken });

vi.advanceTimersByTime(30001);

await waitFor(() =>
expect(toastsError).toHaveBeenCalledWith({
msg: { text: en.send.error.qr_scan_timeout }
})
);

vi.useRealTimers();
});
});

0 comments on commit f35a218

Please sign in to comment.