diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..3b7c061d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,22 @@ +### Issue/Feature + +### Reproduce +Issue exists on version `1.` of sdk-dapp-core-ui. + +### Root cause + +### Fix + +### Additional changes + +### Contains breaking changes +[x] No + +[] Yes + +### Updated CHANGELOG +[x] Yes + +### Testing +[x] User testing +[] Unit tests diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 00000000..cb32836d --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,41 @@ +name: Build and test + +on: + pull_request: + branches: [main, development] + paths: + - 'src/**' + - '**.js' + - '**.ts' + - '**.json' + repository_dispatch: + types: run-unit-tests + workflow_dispatch: + +permissions: + contents: write + +jobs: + build-test: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v1 + with: + node-version: 18 + registry-url: https://registry.npmjs.org/ + + - name: Setup yarn + run: npm install -g yarn + + - name: Install dependencies + run: yarn install + + - name: Run tests + run: yarn test --silent + + - name: Build project + run: yarn build diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 00000000..1b1502a2 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,16 @@ +name: "CHANGELOG entry secretary" +on: + pull_request: + branches: [main, development] + # The specific activity types are listed here to include "labeled" and "unlabeled" + # (which are not included by default for the "pull_request" trigger). + # This is needed to allow skipping enforcement of the changelog in PRs with specific labels, + # as defined in the (optional) "skipLabels" property. + types: [opened, synchronize, reopened, ready_for_review, labeled, unlabeled] + +jobs: + # Enforces the update of a changelog file on every pull request + changelog: + runs-on: ubuntu-latest + steps: + - uses: dangoslen/changelog-enforcer@v3 \ No newline at end of file diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml new file mode 100644 index 00000000..5a78eec1 --- /dev/null +++ b/.github/workflows/npm-publish.yml @@ -0,0 +1,52 @@ +name: Publish sdk-dapp-core-ui + +on: + push: + branches: [main] + repository_dispatch: + types: publish-npm + workflow_dispatch: + +permissions: + contents: write + +jobs: + publish-npm: + runs-on: ubuntu-latest + permissions: + contents: write + packages: write + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v1 + with: + node-version: 18 + registry-url: https://registry.npmjs.org/ + + - name: Setup yarn + run: npm install -g yarn + + - name: Install dependencies + run: yarn install + + - name: Run tests + run: yarn test + + - name: Build project + run: yarn build + + - name: Get package info + id: package + uses: andreigiura/action-nodejs-package-info@v1.0.2 + + - name: Publish to npmjs next version + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + if: ${{ steps.package.outputs.is-prerelease == 'true'}} + run: echo ${{ steps.package.outputs.is-prerelease}} && npm publish --tag next + + - name: Publish to npmjs + env: + NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} + if: ${{ steps.package.outputs.is-prerelease == 'false' }} + run: npm publish diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..48e3dd31 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,13 @@ +# Change Log + +All notable changes will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [[0.0.0-alpha.1](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/16)] - 2025-01-20 + +- [Updated formatAmount component and removed dependencies](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/17) +- [Updated formatAmount component](https://github.com/multiversx/mx-sdk-dapp-core-ui/pull/15) \ No newline at end of file diff --git a/package.json b/package.json index b8bd4ec8..0cd7942f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@multiversx/sdk-dapp-core-ui", - "version": "0.0.0-alpha.0", + "version": "0.0.0-alpha.1", "description": "A library to hold UI components for a dApp on the MultiversX blockchain", "author": "MultiversX", "license": "MIT", @@ -53,8 +53,7 @@ "dependencies": { "@fortawesome/fontawesome-svg-core": ">= 6.7.2", "@fortawesome/free-solid-svg-icons": ">= 6.7.2", - "@multiversx/sdk-dapp-utils": ">= 1.0.2", - "bignumber.js": ">= 9.1.2", + "@multiversx/sdk-dapp-utils": ">= 1.0.4", "classnames": ">= 2.5.1", "qrcode": ">= 1.5.4" }, diff --git a/src/common/generic-modal/generic-modal.css b/src/common/generic-modal/generic-modal.css index 6603232d..f51d5cb4 100644 --- a/src/common/generic-modal/generic-modal.css +++ b/src/common/generic-modal/generic-modal.css @@ -1,33 +1,34 @@ :host { - display: block; - } - - .modal { - position: fixed; - z-index: 1000; - left: 0; - top: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.4); - } - - .modal-content { - background-color: #ffffff; - margin: 5% auto; - padding: 20px; - border-radius: 12px; - width: 90%; - max-width: 600px; - } - - .modal-header { - text-align: center; - margin-bottom: 20px; - } - - .close { - float: right; - cursor: pointer; - padding: 5px; - } + display: block; +} + +.modal { + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.4); + overflow: auto; +} + +.modal-content { + background-color: #ffffff; + margin: 5% auto; + padding: 20px; + border-radius: 12px; + width: 90%; + max-width: 600px; +} + +.modal-header { + text-align: center; + margin-bottom: 20px; +} + +.close { + float: right; + cursor: pointer; + padding: 5px; +} diff --git a/src/components.d.ts b/src/components.d.ts index b8e889e1..5721a2d0 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -28,13 +28,11 @@ export namespace Components { } interface FormatAmount { "class"?: string; - "decimals"?: number; - "digits"?: number; - "egldLabel"?: string; - "showLabel"?: boolean; - "showLastNonZeroDecimal"?: boolean; - "token"?: string; - "value": string; + "isValid": boolean; + "label"?: string; + "labelClass"?: string; + "valueDecimal": string; + "valueInteger": string; } interface FungibleComponent { } @@ -293,13 +291,11 @@ declare namespace LocalJSX { } interface FormatAmount { "class"?: string; - "decimals"?: number; - "digits"?: number; - "egldLabel"?: string; - "showLabel"?: boolean; - "showLastNonZeroDecimal"?: boolean; - "token"?: string; - "value"?: string; + "isValid"?: boolean; + "label"?: string; + "labelClass"?: string; + "valueDecimal"?: string; + "valueInteger"?: string; } interface FungibleComponent { } diff --git a/src/components/format-amount/format-amount.tsx b/src/components/format-amount/format-amount.tsx index bca38c25..60f2189e 100644 --- a/src/components/format-amount/format-amount.tsx +++ b/src/components/format-amount/format-amount.tsx @@ -1,7 +1,4 @@ import { Component, Prop, h } from '@stencil/core'; -import BigNumber from 'bignumber.js'; -import { formatAmount } from '@multiversx/sdk-dapp-utils/out/helpers'; -import { DECIMALS, DIGITS, ZERO } from '@multiversx/sdk-dapp-utils/out/constants'; @Component({ tag: 'format-amount', @@ -10,13 +7,11 @@ import { DECIMALS, DIGITS, ZERO } from '@multiversx/sdk-dapp-utils/out/constants }) export class FormatAmount { @Prop() class?: string; - @Prop() decimals?: number = DECIMALS; - @Prop() digits?: number = DIGITS; - @Prop() egldLabel?: string; - @Prop() showLabel?: boolean = true; - @Prop() showLastNonZeroDecimal?: boolean = false; - @Prop() token?: string; - @Prop() value: string; + @Prop() isValid: boolean; + @Prop() label?: string; + @Prop() labelClass?: string; + @Prop() valueDecimal: string; + @Prop() valueInteger: string; private renderInvalid() { return ( @@ -29,41 +24,25 @@ export class FormatAmount { } private renderValid() { - const formattedValue = formatAmount({ - input: this.value, - decimals: this.decimals, - digits: this.digits, - showLastNonZeroDecimal: this.showLastNonZeroDecimal, - addCommas: true - }); - - const valueParts = formattedValue.split('.'); - const hasNoDecimals = valueParts.length === 1; - const isNotZero = formattedValue !== ZERO; - - if (this.digits > 0 && hasNoDecimals && isNotZero) { - valueParts.push(ZERO.repeat(this.digits)); - } - return ( - {valueParts[0]} + {this.valueInteger} - {valueParts.length > 1 && ( + {this.valueDecimal && ( - .{valueParts[1]} + .{this.valueDecimal} )} - {this.showLabel && ( + {this.label && ( - {` ${this.token ?? this.egldLabel}`} + {this.label} )} @@ -71,7 +50,6 @@ export class FormatAmount { } render() { - const isInteger = new BigNumber(this.value).isInteger(); - return !isInteger ? this.renderInvalid() : this.renderValid(); + return this.isValid ? this.renderValid() : this.renderInvalid(); } } diff --git a/src/components/format-amount/readme.md b/src/components/format-amount/readme.md index b26ccf11..645ff7d9 100644 --- a/src/components/format-amount/readme.md +++ b/src/components/format-amount/readme.md @@ -7,16 +7,14 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| ------------------------ | ---------------------------- | ----------- | --------- | ----------- | -| `class` | `class` | | `string` | `undefined` | -| `decimals` | `decimals` | | `number` | `DECIMALS` | -| `digits` | `digits` | | `number` | `DIGITS` | -| `egldLabel` | `egld-label` | | `string` | `undefined` | -| `showLabel` | `show-label` | | `boolean` | `true` | -| `showLastNonZeroDecimal` | `show-last-non-zero-decimal` | | `boolean` | `false` | -| `token` | `token` | | `string` | `undefined` | -| `value` | `value` | | `string` | `undefined` | +| Property | Attribute | Description | Type | Default | +| -------------- | --------------- | ----------- | --------- | ----------- | +| `class` | `class` | | `string` | `undefined` | +| `isValid` | `is-valid` | | `boolean` | `undefined` | +| `label` | `label` | | `string` | `undefined` | +| `labelClass` | `label-class` | | `string` | `undefined` | +| `valueDecimal` | `value-decimal` | | `string` | `undefined` | +| `valueInteger` | `value-integer` | | `string` | `undefined` | ---------------------------------------------- diff --git a/src/components/format-amount/tests/format-amount.spec.ts b/src/components/format-amount/tests/format-amount.spec.ts index a72b28f3..49440fac 100644 --- a/src/components/format-amount/tests/format-amount.spec.ts +++ b/src/components/format-amount/tests/format-amount.spec.ts @@ -1,17 +1,16 @@ import { newSpecPage } from '@stencil/core/testing'; import { FormatAmount } from '../format-amount'; -describe('Format amount component when digits = 2', () => { +describe('FormatAmount component', () => { const renderComponent = async (props: any) => { const page = await newSpecPage({ components: [FormatAmount], html: '', - supportsShadowDom: true + supportsShadowDom: true, }); const component = page.root; - // Set each property individually - Object.keys(props).forEach(key => { + Object.keys(props).forEach((key) => { component[key] = props[key]; }); @@ -31,81 +30,64 @@ describe('Format amount component when digits = 2', () => { .length; }; - it('should show 2 non zero decimals', async () => { + it('should render valid amount with decimals', async () => { const props = { - value: '9999979999800000000000000', - showLastNonZeroDecimal: false, - showLabel: true, - digits: 2, - egldLabel: 'EGLD' + isValid: true, + valueInteger: '99999', + valueDecimal: '99', + label: 'EGLD', }; const page = await renderComponent(props); - expect(await decimalsSelector(page)).toBe('.99'); + expect(page.root.shadowRoot.querySelector('span[data-testid="formatAmountInt"]').textContent).toBe('99999'); + expect(decimalsSelector(page)).toBe('.99'); }); - it('should show 2 zero decimals', async () => { + it('should not render decimals when valueDecimal is empty', async () => { const props = { - value: '9000000000000000000000000', - showLastNonZeroDecimal: false, - showLabel: true, - digits: 2, - egldLabel: 'EGLD' + isValid: true, + valueInteger: '90000', + valueDecimal: '', + label: 'EGLD', }; const page = await renderComponent(props); - expect(await decimalsSelector(page)).toBe('.00'); + expect(decimalsSelector(page)).toBe(undefined); }); - it('should show all non zero decimals when showLastNonZeroDecimal = true', async () => { + it('should render invalid state when isValid is false', async () => { const props = { - value: '100000000000000', - showLastNonZeroDecimal: true, - showLabel: false, - digits: 2, - egldLabel: 'EGLD' + isValid: false, + valueInteger: '', + valueDecimal: '', + label: '', }; const page = await renderComponent(props); - expect(await decimalsSelector(page)).toBe('.0001'); + expect(page.root.shadowRoot.querySelector('span[data-testid="formatAmountInt"]').textContent).toBe('...'); }); - it('should not show decimals when value is 0', async () => { + it('should render symbol when label is provided', async () => { const props = { - value: '100000000000000', - showLastNonZeroDecimal: false, - showLabel: true, - digits: 2, - egldLabel: 'EGLD' + isValid: true, + valueInteger: '90000', + valueDecimal: '00', + label: 'EGLD', }; const page = await renderComponent(props); - expect(await decimalsSelector(page)).toBe(undefined); + expect(symbolSelector(page)).toBe(1); }); - it('should show symbol', async () => { + it('should not render symbol when label is not provided', async () => { const props = { - value: '9000000000000000000000000', - showLastNonZeroDecimal: false, - showLabel: true, - digits: 2, - egldLabel: 'EGLD' + isValid: true, + valueInteger: '90000', + valueDecimal: '00', + label: '', }; const page = await renderComponent(props); - expect(await symbolSelector(page)).toBe(1); - }); - - it('should not show symbol', async () => { - const props = { - value: '9000000000000000000000000', - showLastNonZeroDecimal: false, - showLabel: false, - digits: 2, - egldLabel: 'EGLD' - }; - - const page = await renderComponent(props); - expect(await symbolSelector(page)).toBe(0); + expect(symbolSelector(page)).toBe(0); }); }); diff --git a/src/components/ledger-connect-modal/ledger-connect-modal.css b/src/components/ledger-connect-modal/ledger-connect-modal.css index 8e42ff47..53438568 100644 --- a/src/components/ledger-connect-modal/ledger-connect-modal.css +++ b/src/components/ledger-connect-modal/ledger-connect-modal.css @@ -29,7 +29,9 @@ .account-list { width: 100%; min-height: 300px; + max-height: 300px; position: relative; + overflow: auto; } .account-row { @@ -147,6 +149,7 @@ padding: 12px; text-align: center; border-radius: 8px; + word-break: break-word; } .ledger-confirm-address-section .ledger-confirm-address-header { diff --git a/src/components/sign-transactions-modal/components/fungible-component/fungible-component.tsx b/src/components/sign-transactions-modal/components/fungible-component/fungible-component.tsx index fcee75e2..b1d2f965 100644 --- a/src/components/sign-transactions-modal/components/fungible-component/fungible-component.tsx +++ b/src/components/sign-transactions-modal/components/fungible-component/fungible-component.tsx @@ -1,22 +1,26 @@ import { Component, h } from '@stencil/core'; import state from '../../signTransactionsModalStore'; +import { NftEnumType } from 'types/tokens.types'; const LABELS = { - SemiFungibleESDT: 'SFT', - NonFungibleESDT: 'NFT', + [NftEnumType.SemiFungibleESDT]: 'SFT', + [NftEnumType.NonFungibleESDT]: 'NFT' }; @Component({ tag: 'fungible-component', styleUrl: 'fungible-component.css', - shadow: false, + shadow: false }) export class FungibleComponent { render() { const { sftTransaction, nftTransaction, commonData } = state; const { tokenType } = commonData; - const data = tokenType === 'SemiFungibleESDT' ? sftTransaction : nftTransaction; + const data = + tokenType === NftEnumType.SemiFungibleESDT + ? sftTransaction + : nftTransaction; const { amount = '', identifier = '', imageURL = '' } = data || {}; const label = LABELS[tokenType]; diff --git a/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.css b/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.css index 82b574b8..140d23b4 100644 --- a/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.css +++ b/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.css @@ -7,8 +7,8 @@ .transaction-container { display: flex; flex-direction: column; - padding: 1rem; - gap: 2rem; + padding: 0rem 1rem 0rem 1rem; + gap: 1rem; } .receiver-container { @@ -27,12 +27,25 @@ line-height: 1; } -.data-content { +.data-content-container { + display: block; width: 100%; + min-height: 100px; height: 100px; - resize: none; - border-radius: 0.5rem; - padding: 0.5rem; + border: 1px solid #ccc; + border-radius: 4px; + padding: 8px; + overflow-y: auto; + font-family: inherit; + font-size: inherit; + background-color: #f9f9f9; + white-space: pre-wrap; + word-wrap: break-word; + color: #333; +} + +.data-content { + opacity: 0.6; } .fee-container { @@ -43,6 +56,39 @@ line-height: 1; } +.sign-button-container { + display: flex; + flex-direction: column; +} + +.sign-back-button { + display: inline-block; + padding: 8px 12px; + background-color: transparent; + color: #1a56db; + border: none; + border-radius: 4px; + cursor: pointer; + text-decoration: none; + font-size: 16px; + + &:hover { + text-decoration: underline; + color: #0033a0; + } + + &:active { + color: #001f70; + } + + &:disabled { + color: #a1a1a1; + cursor: not-allowed; + text-decoration: none; + opacity: 0.6; + } +} + .sign-button { display: block; width: 200px; @@ -63,3 +109,7 @@ transform: none; } } + +.highlight { + opacity: 1; +} diff --git a/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.tsx b/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.tsx index 16d4175e..d805eac1 100644 --- a/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.tsx +++ b/src/components/sign-transactions-modal/components/sign-transaction-component/sign-transaction-component.tsx @@ -6,14 +6,86 @@ import state from '../../signTransactionsModalStore'; @Component({ tag: 'sign-transaction-component', styleUrl: 'sign-transaction-component.css', - shadow: false, + shadow: false }) export class SignTransaction { @Prop() header: VNode; + getSignButtonProps() { + const { currentIndex, nextUnsignedTxIndex } = state.commonData; + + if (currentIndex === nextUnsignedTxIndex) { + return { + signText: 'Sign', + disabled: state.isWaitingForSignature, + 'data-testid': DataTestIdsEnum.signTransactionBtn, + onClick: state.onSign + }; + } + + return { + signText: 'Next', + disabled: false, + 'data-testid': DataTestIdsEnum.signNextTransactionBtn, + onClick: state.onNext + }; + } + + getBackButtonProps() { + const { transactionsCount, currentIndex } = state.commonData; + const isMultipleTransactions = transactionsCount > 1; + + if (!isMultipleTransactions) { + return {}; + } + + if (currentIndex === 0) { + return { + 'data-testid': DataTestIdsEnum.signCancelBtn, + backButtonText: 'Cancel', + onClick: state.onCancel + }; + } + + return { + 'data-testid': DataTestIdsEnum.signBackBtn, + backButtonText: 'Back', + onClick: state.onPrev, + disabled: state.isWaitingForSignature + }; + } + + getHighlightedData() { + const { data, highlight } = state.commonData; + + if (!highlight || !data) { + return data; + } + + const parts = data.split(highlight); + + return ( + + {parts.map((part, index) => ( + + {part} + {index < parts.length - 1 && ( + {highlight} + )} + + ))} + + ); + } + render() { - const { receiver, egldLabel, data, feeInFiatLimit, feeLimit } = state.commonData; + const { receiver, egldLabel, feeInFiatLimit, feeLimit, scCall } = + state.commonData; + const { signText, ...signButtonProps } = this.getSignButtonProps(); + const { backButtonText, ...backButtonProps } = this.getBackButtonProps(); + + const highlightedData = this.getHighlightedData(); return (
{this.header} @@ -34,16 +106,29 @@ export class SignTransaction {

≈{feeInFiatLimit}

+ {scCall && ( +
+

Smart Contract Call

+
{scCall}
+
+ )} +

Data

- +
{highlightedData}
- +
+ + + {backButtonText && ( + + )} +
); } diff --git a/src/components/sign-transactions-modal/readme.md b/src/components/sign-transactions-modal/readme.md index c39a6cc2..640f6e84 100644 --- a/src/components/sign-transactions-modal/readme.md +++ b/src/components/sign-transactions-modal/readme.md @@ -7,9 +7,9 @@ ## Properties -| Property | Attribute | Description | Type | Default | -| -------- | --------- | ----------- | ---------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `data` | -- | | `ISignTransactionsModalData` | `{ commonData: { egldLabel: '', feeLimit: '', feeInFiatLimit: '', transactionsCount: 0, currentIndex: 0 }, tokenTransaction: null, nftTransaction: null, sftTransaction: null, }` | +| Property | Attribute | Description | Type | Default | +| -------- | --------- | ----------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `data` | -- | | `ISignTransactionsModalData` | `{ commonData: { egldLabel: '', feeLimit: '', feeInFiatLimit: '', transactionsCount: 0, currentIndex: 0 }, tokenTransaction: null, nftTransaction: null, sftTransaction: null }` | ## Methods diff --git a/src/components/sign-transactions-modal/sign-transactions-modal.tsx b/src/components/sign-transactions-modal/sign-transactions-modal.tsx index ce7ca441..aa147ac8 100644 --- a/src/components/sign-transactions-modal/sign-transactions-modal.tsx +++ b/src/components/sign-transactions-modal/sign-transactions-modal.tsx @@ -1,28 +1,46 @@ -import { Component, Prop, h, Element, Method, forceUpdate, Watch } from '@stencil/core'; +import { + Component, + Prop, + h, + Element, + Method, + forceUpdate, + Watch +} from '@stencil/core'; import { EventBus, IEventBus } from 'utils/EventBus'; -import { ISignTransactionsModalData, SignEventsEnum } from './sign-transactions-modal.types'; +import { + ISignTransactionsModalData, + SignEventsEnum +} from './sign-transactions-modal.types'; import state, { resetState } from './signTransactionsModalStore'; const signScreens = { FungibleESDT: 'token-component', SemiFungibleESDT: 'fungible-component', NonFungibleESDT: 'fungible-component', + MetaESDT: 'token-component' }; @Component({ tag: 'sign-transactions-modal', styleUrl: 'sign-transactions-modal.css', - shadow: true, + shadow: true }) export class SignTransactionsModal { @Element() hostElement: HTMLElement; private eventBus: IEventBus = new EventBus(); @Prop() data: ISignTransactionsModalData = { - commonData: { egldLabel: '', feeLimit: '', feeInFiatLimit: '', transactionsCount: 0, currentIndex: 0 }, + commonData: { + egldLabel: '', + feeLimit: '', + feeInFiatLimit: '', + transactionsCount: 0, + currentIndex: 0 + }, tokenTransaction: null, nftTransaction: null, - sftTransaction: null, + sftTransaction: null }; @Method() async getEventBus() { @@ -46,6 +64,18 @@ export class SignTransactionsModal { state.isWaitingForSignature = true; this.eventBus.publish(SignEventsEnum.SIGN_TRANSACTION); }; + + state.onPrev = () => { + this.eventBus.publish(SignEventsEnum.PREV_TRANSACTION); + }; + + state.onNext = () => { + this.eventBus.publish(SignEventsEnum.NEXT_TRANSACTION); + }; + + state.onCancel = () => { + this.eventBus.publish(SignEventsEnum.CLOSE); + }; } render() { @@ -71,14 +101,6 @@ export class SignTransactionsModal { ); } - async nextPage() { - this.eventBus.publish(SignEventsEnum.NEXT_PAGE); - } - - async prevPage() { - this.eventBus.publish(SignEventsEnum.PREV_PAGE); - } - close(props = { isUserClick: true }) { resetState(); @@ -97,11 +119,17 @@ export class SignTransactionsModal { } componentDidLoad() { - this.eventBus.subscribe(SignEventsEnum.DATA_UPDATE, this.dataUpdate.bind(this)); + this.eventBus.subscribe( + SignEventsEnum.DATA_UPDATE, + this.dataUpdate.bind(this) + ); } disconnectedCallback() { resetState(); - this.eventBus.unsubscribe(SignEventsEnum.DATA_UPDATE, this.dataUpdate.bind(this)); + this.eventBus.unsubscribe( + SignEventsEnum.DATA_UPDATE, + this.dataUpdate.bind(this) + ); } } diff --git a/src/components/sign-transactions-modal/sign-transactions-modal.types.ts b/src/components/sign-transactions-modal/sign-transactions-modal.types.ts index 1c328ac5..38ffea7a 100644 --- a/src/components/sign-transactions-modal/sign-transactions-modal.types.ts +++ b/src/components/sign-transactions-modal/sign-transactions-modal.types.ts @@ -1,3 +1,6 @@ +// types here need to be synced with the types in sdk-dapp-core signTransactionsModal.types.ts +import { EsdtEnumType, NftEnumType } from 'types/tokens.types'; + export interface ITransactionData { receiver?: string; data?: string; @@ -10,7 +13,7 @@ export type FungibleTransactionType = { imageURL: string; }; -export type TokenType = 'SemiFungibleESDT' | 'NonFungibleESDT' | 'FungibleESDT' | null; +export type TokenType = EsdtEnumType | NftEnumType; export interface ISignTransactionsModalData { shouldClose?: true; @@ -18,15 +21,17 @@ export interface ISignTransactionsModalData { receiver?: string; data?: string; transactionsCount: number; - /** - * Token type of the transaction. - * @param {string} `null` - if is EGLD or MultiEsdt transaction. - */ tokenType?: TokenType; egldLabel: string; feeLimit?: string; feeInFiatLimit?: string | null; currentIndex: number; + /** + * Tracks the index of the next unsigned transaction to be processed. + */ + nextUnsignedTxIndex?: number; + highlight?: string; + scCall?: string; }; tokenTransaction: { identifier?: string; @@ -39,8 +44,8 @@ export interface ISignTransactionsModalData { export enum SignEventsEnum { 'SIGN_TRANSACTION' = 'SIGN_TRANSACTION', - 'NEXT_PAGE' = 'NEXT_PAGE', - 'PREV_PAGE' = 'PREV_PAGE', + 'NEXT_TRANSACTION' = 'NEXT_TRANSACTION', + 'PREV_TRANSACTION' = 'PREV_TRANSACTION', 'CLOSE' = 'CLOSE', - 'DATA_UPDATE' = 'DATA_UPDATE', + 'DATA_UPDATE' = 'DATA_UPDATE' } diff --git a/src/components/sign-transactions-modal/signTransactionsModalStore.ts b/src/components/sign-transactions-modal/signTransactionsModalStore.ts index 48f6ba86..d7a37c0a 100644 --- a/src/components/sign-transactions-modal/signTransactionsModalStore.ts +++ b/src/components/sign-transactions-modal/signTransactionsModalStore.ts @@ -5,6 +5,9 @@ type ITransactionState = ISignTransactionsModalData & { isLoading: boolean; isWaitingForSignature: boolean; onSign: () => void; + onCancel: () => void; + onPrev: () => void; + onNext: () => void; }; const initialState: ITransactionState = { @@ -18,20 +21,24 @@ const initialState: ITransactionState = { feeInFiatLimit: '', currentIndex: 0, receiver: '', + nextUnsignedTxIndex: 0 }, nftTransaction: null, sftTransaction: null, tokenTransaction: null, onSign: null, + onCancel: null, + onPrev: null, + onNext: null }; const { state } = createStore({ - ...initialState, + ...initialState }); export const resetState = () => ({ ...state, - ...initialState, + ...initialState }); export default state; diff --git a/src/constants/dataTestIds.enum.ts b/src/constants/dataTestIds.enum.ts index 5f35fd7d..fb3bfc30 100644 --- a/src/constants/dataTestIds.enum.ts +++ b/src/constants/dataTestIds.enum.ts @@ -17,4 +17,7 @@ export enum DataTestIdsEnum { transactionToastTitle = 'transactionToastTitle', transactionDetailsToastBody = 'transactionDetailsToastBody', signTransactionBtn = 'signTransactionBtn', + signNextTransactionBtn = 'signNextTransactionBtn', + signCancelBtn = 'signCancelBtn', + signBackBtn = 'signBackBtn' } diff --git a/src/index.html b/src/index.html index 8015d4e4..4b5d1151 100644 --- a/src/index.html +++ b/src/index.html @@ -11,11 +11,21 @@ + +