diff --git a/package.json b/package.json index 00b10f35e..4b1e8aa31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "polkaswap-exchange-web", - "version": "1.43.1", + "version": "1.44.0", "repository": { "type": "git", "url": "https://github.com/sora-xor/polkaswap-exchange-web.git" @@ -30,7 +30,7 @@ "dependencies": { "@cedelabs/widgets-universal": "^1.3.1", "@metamask/detect-provider": "^2.0.0", - "@soramitsu/soraneo-wallet-web": "1.43.5", + "@soramitsu/soraneo-wallet-web": "1.44.1", "@walletconnect/ethereum-provider": "^2.13.3", "@walletconnect/modal": "^2.6.2", "country-code-emoji": "^2.3.0", diff --git a/src/App.vue b/src/App.vue index 220a79302..059272679 100644 --- a/src/App.vue +++ b/src/App.vue @@ -121,7 +121,6 @@ export default class App extends Mixins(mixins.TransactionMixin, NodeErrorMixin) @state.settings.browserNotifPopupBlockedVisibility private browserNotifPopupBlocked!: boolean; @state.settings.isOrientationWarningVisible private orientationWarningVisible!: boolean; @state.settings.isThemePreference isThemePreference!: boolean; - @state.settings.featureFlags private featureFlags!: FeatureFlags; @state.settings.isTMA isTMA!: boolean; @state.wallet.account.assetsToNotifyQueue private assetsToNotifyQueue!: Array; @state.referrals.storageReferrer private storageReferrer!: string; diff --git a/src/components/mixins/IndexerDataFetchMixin.ts b/src/components/mixins/IndexerDataFetchMixin.ts index 83edf4857..507927309 100644 --- a/src/components/mixins/IndexerDataFetchMixin.ts +++ b/src/components/mixins/IndexerDataFetchMixin.ts @@ -1,6 +1,6 @@ import { mixins, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; import isEqual from 'lodash/fp/isEqual'; -import { Component, Mixins } from 'vue-property-decorator'; +import { Component, Mixins, Watch } from 'vue-property-decorator'; import { type FetchVariables } from '@/types/indexers'; import { debouncedInputHandler } from '@/utils'; @@ -8,14 +8,20 @@ import { debouncedInputHandler } from '@/utils'; @Component export default class IndexerDataFetchMixin extends Mixins(mixins.LoadingMixin, mixins.PaginationSearchMixin) { totalCount = 0; - items: any[] = []; + items: readonly any[] = []; + + pageAmount = 5; + fetchAmount = 5; - intervalTimestamp = 0; private interval: Nullable> = null; private updateItems = debouncedInputHandler(this.updateData, 250, { leading: false }); updateInterval = 24_000; // 4 blocks + get intervalTimestamp(): number { + return Math.floor(this.getItemTimestamp(this.items[0]) / 1000); + } + get loadingState(): boolean { return this.parentLoading || this.loading; } @@ -37,6 +43,23 @@ export default class IndexerDataFetchMixin extends Mixins(mixins.LoadingMixin, m return {}; } + get fetchPage(): number { + return Math.ceil((this.pageAmount * this.currentPage) / this.fetchAmount); + } + + get visibleItems(): any[] { + const offset = this.fetchAmount * (this.fetchPage - 1); + const start = this.pageAmount * (this.currentPage - 1) - offset; + const end = start + this.pageAmount; + + return this.items.slice(start, end); + } + + @Watch('fetchPage') + private checkPageToLoad(): void { + this.updateItems(); + } + checkTriggerUpdate(current: any, prev: any) { if (!isEqual(current)(prev)) { this.resetPage(); @@ -55,11 +78,6 @@ export default class IndexerDataFetchMixin extends Mixins(mixins.LoadingMixin, m return 0; } - async onPaginationClick(button: WALLET_CONSTS.PaginationButton): Promise { - this.handlePaginationClick(button); - this.updateItems(); - } - public handlePaginationClick(button: WALLET_CONSTS.PaginationButton): void { let current = 1; @@ -83,8 +101,7 @@ export default class IndexerDataFetchMixin extends Mixins(mixins.LoadingMixin, m await this.fetchData(); - if (this.currentPage === 1) { - this.updateIntervalTimestamp(); + if (this.fetchPage === 1) { this.subscribeOnData(); } } @@ -98,21 +115,16 @@ export default class IndexerDataFetchMixin extends Mixins(mixins.LoadingMixin, m await this.withLoading(async () => { await this.withParentLoading(async () => { const { totalCount, items } = await this.requestData(this.dataVariables); + this.items = Object.freeze([...items]); this.totalCount = totalCount; - this.items = items; }); }); } private async fetchDataUpdates(): Promise { const { items, totalCount } = await this.requestData(this.updateVariables); - this.items = [...items, ...this.items].slice(0, this.pageAmount); + this.items = Object.freeze([...items, ...this.items].slice(0, this.fetchAmount)); this.totalCount = this.totalCount + totalCount; - this.updateIntervalTimestamp(); - } - - private updateIntervalTimestamp(): void { - this.intervalTimestamp = Math.floor(this.getItemTimestamp(this.items[0]) / 1000); } private subscribeOnData(): void { diff --git a/src/components/mixins/NetworkFormatterMixin.ts b/src/components/mixins/NetworkFormatterMixin.ts index d9b2e4015..3776bce4f 100644 --- a/src/components/mixins/NetworkFormatterMixin.ts +++ b/src/components/mixins/NetworkFormatterMixin.ts @@ -10,8 +10,7 @@ import { state, getter } from '@/store/decorators'; import type { AvailableNetwork } from '@/store/web3/types'; import type { NetworkData } from '@/types/bridge'; import { getSubstrateExplorerLinks } from '@/utils'; -import { isOutgoingTransaction } from '@/utils/bridge/common/utils'; -import { isUnsignedToPart } from '@/utils/bridge/eth/utils'; +import { isOutgoingTransaction, isWaitingForAction } from '@/utils/bridge/common/utils'; import TranslationMixin from './TranslationMixin'; @@ -228,7 +227,7 @@ export default class NetworkFormatterMixin extends Mixins(TranslationMixin) { isWaitingForAction(item: Nullable): boolean { if (!item) return false; // ETH - return item.transactionState === ETH_BRIDGE_STATES.EVM_REJECTED && isUnsignedToPart(item); + return isWaitingForAction(item); } formatDatetime(item: Nullable): string { diff --git a/src/components/pages/Bridge/NetworkSelector.vue b/src/components/pages/Bridge/NetworkSelector.vue new file mode 100644 index 000000000..5349d155f --- /dev/null +++ b/src/components/pages/Bridge/NetworkSelector.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/components/pages/OrderBook/Popovers/PairListPopover.vue b/src/components/pages/OrderBook/Popovers/PairListPopover.vue index 0ba835bd1..172afe10d 100644 --- a/src/components/pages/OrderBook/Popovers/PairListPopover.vue +++ b/src/components/pages/OrderBook/Popovers/PairListPopover.vue @@ -18,7 +18,7 @@ :highlight-current-row="false" @row-click="chooseBook" > - + @@ -29,7 +29,7 @@ - + @@ -45,15 +45,15 @@ - + - + @@ -92,12 +92,14 @@ import type { OrderBook, OrderBookId } from '@sora-substrate/liquidity-proxy'; import type { AccountAsset } from '@sora-substrate/sdk/build/assets/types'; interface BookFields { + id: OrderBookId; pair: string; baseAsset?: Nullable; targetAsset?: Nullable; - price?: string; - priceChange?: FPNumber; - volume?: string; + price: string; + priceChange: FPNumber; + volumeNumber: number; + volume: string; status: string; } @@ -114,12 +116,11 @@ export default class PairListPopover extends Mixins( mixins.LoadingMixin, mixins.FormattedAmountMixin ) { - @state.orderBook.orderBooks orderBooks!: Record; - @state.orderBook.orderBooksStats orderBooksStats!: Record; + @state.orderBook.orderBooks private orderBooks!: Record; + @state.orderBook.orderBooksStats private orderBooksStats!: Record; - @getter.assets.assetDataByAddress getAsset!: (addr?: string) => Nullable; - - @mutation.orderBook.setCurrentOrderBook setCurrentOrderBook!: (orderBookId: OrderBookId) => void; + @getter.assets.assetDataByAddress private getAsset!: (addr?: string) => Nullable; + @mutation.orderBook.setCurrentOrderBook private setCurrentOrderBook!: (id: OrderBookId) => void; getTooltipText(status: OrderBookStatus): string { switch (status) { @@ -136,8 +137,8 @@ export default class PairListPopover extends Mixins( } } - chooseBook(row): void { - this.setCurrentOrderBook(row.orderBookId); + chooseBook(row: BookFields): void { + this.setCurrentOrderBook(row.id); this.$emit('close'); } @@ -148,20 +149,33 @@ export default class PairListPopover extends Mixins( if (!orderBookId) return buffer; const { base, quote } = value.orderBookId; const decimals = getBookDecimals(value); - const price = this.orderBooksStats[orderBookId]?.price ?? FPNumber.ZERO; + const price = (this.orderBooksStats[orderBookId]?.price ?? FPNumber.ZERO).dp(decimals); const priceChange = this.orderBooksStats[orderBookId]?.priceChange ?? FPNumber.ZERO; const volume = this.orderBooksStats[orderBookId]?.volume ?? FPNumber.ZERO; - const row = { - orderBookId: value.orderBookId, - baseAsset: this.getAsset(base), - targetAsset: this.getAsset(quote), - pair: `${this.getAsset(base)?.symbol}-${this.getAsset(quote)?.symbol}`, + const baseAsset = this.getAsset(base); + const targetAsset = this.getAsset(quote); + const row: BookFields = { + id: value.orderBookId, + baseAsset, + targetAsset, + pair: `${baseAsset?.symbol}-${targetAsset?.symbol}`, status: value.status, - price: price.dp(decimals).toLocaleString(), + price: price.toLocaleString(), priceChange, + volumeNumber: volume.toNumber(), volume: volume.toLocaleString(), }; - buffer.push(row); + const index = buffer.findIndex( + (item) => + row.status > item.status || + (row.status === item.status && row.id.dexId > item.id.dexId) || + (row.status === item.status && row.id.dexId === item.id.dexId && row.volumeNumber > item.volumeNumber) + ); + if (index !== -1) { + buffer.splice(index, 0, row); + } else { + buffer.push(row); + } return buffer; }, []); } diff --git a/src/components/pages/OrderBook/TransactionDetails.vue b/src/components/pages/OrderBook/TransactionDetails.vue index 4e5abc84e..b498705f2 100644 --- a/src/components/pages/OrderBook/TransactionDetails.vue +++ b/src/components/pages/OrderBook/TransactionDetails.vue @@ -4,7 +4,7 @@ :label="t('orderBook.txDetails.orderType')" :label-tooltip="t('orderBook.tooltip.txDetails.orderType')" :value="sideText" - :class="getComputedClass()" + :class="computedClass" /> @@ -69,27 +69,29 @@ import type { AccountAsset } from '@sora-substrate/sdk/build/assets/types'; export default class PlaceTransactionDetails extends Mixins(mixins.FormattedAmountMixin, TranslationMixin) { @state.orderBook.baseValue baseValue!: string; @state.orderBook.quoteValue quoteValue!: string; - @state.orderBook.baseAssetAddress baseAssetAddress!: string; - @state.orderBook.quoteAssetAddress quoteAssetAddress!: string; @state.orderBook.side side!: PriceVariant; @state.swap.toValue toValue!: string; @state.wallet.settings.networkFees private networkFees!: NetworkFeesObject; - - @getter.assets.assetDataByAddress getAsset!: (addr?: string) => AccountAsset; + @getter.orderBook.baseAsset private baseAsset!: AccountAsset; + @getter.orderBook.quoteAsset private quoteAsset!: AccountAsset; @Prop({ default: true, type: Boolean }) readonly infoOnly!: boolean; @Prop({ default: false, type: Boolean }) readonly isMarketType!: boolean; + get xorSymbol(): string { + return XOR.symbol; + } + get networkFee(): CodecString { return this.networkFees[Operation.OrderBookPlaceLimitOrder]; } - get baseSymbol(): string | undefined { - return this.getAsset(this.baseAssetAddress)?.symbol; + get baseSymbol(): string { + return this.baseAsset.symbol; } get quoteSymbol(): string { - return XOR.symbol; + return this.quoteAsset.symbol; } get sideText(): string { @@ -105,18 +107,18 @@ export default class PlaceTransactionDetails extends Mixins(mixins.FormattedAmou } get lockedAsset(): AccountAsset { - return this.isBuy ? this.getAsset(this.quoteAssetAddress) : this.getAsset(this.baseAssetAddress); + return this.isBuy ? this.quoteAsset : this.baseAsset; } get lockedAssetSymbol(): string | undefined { return this.isBuy ? this.quoteSymbol : this.baseSymbol; } - get total(): FPNumber { + private get total(): FPNumber { return this.getFPNumber(this.baseValue).mul(this.getFPNumber(this.quoteValue)); } - get isBuy(): boolean { + private get isBuy(): boolean { return this.side === PriceVariant.Buy; } @@ -130,14 +132,11 @@ export default class PlaceTransactionDetails extends Mixins(mixins.FormattedAmou return this.formatCodecNumber(this.networkFee); } - formatFee(fee: string, formattedFee: string): string { - return fee !== ZeroStringValue ? formattedFee : ZeroStringValue; - } - - getComputedClass(): string | undefined { + get computedClass(): string | undefined { if (this.infoOnly) { return this.side === PriceVariant.Buy ? 'limit-order-type--buy' : 'limit-order-type--sell'; } + return undefined; } } diff --git a/src/components/pages/Swap/Widget/Transactions.vue b/src/components/pages/Swap/Widget/Transactions.vue index 509b85b4a..ba9ced2a4 100644 --- a/src/components/pages/Swap/Widget/Transactions.vue +++ b/src/components/pages/Swap/Widget/Transactions.vue @@ -1,5 +1,21 @@ @@ -36,10 +53,11 @@ {{ t('removeLiquidity.output') }} @@ -98,7 +116,7 @@ :total="total" :loading="loading" :last-page="lastPage" - @pagination-click="onPaginationClick" + @pagination-click="handlePaginationClick" /> @@ -111,6 +129,7 @@ import { Component, Mixins, Watch } from 'vue-property-decorator'; import IndexerDataFetchMixin from '@/components/mixins/IndexerDataFetchMixin'; import ScrollableTableMixin from '@/components/mixins/ScrollableTableMixin'; +import WithTokenSelectMixin from '@/components/mixins/Widget/WithTokenSelect'; import { Components } from '@/consts'; import { lazyComponent } from '@/router'; import { getter, state } from '@/store/decorators'; @@ -127,7 +146,9 @@ type TableItem = { outputAsset: Nullable; outputAssetSymbol: string; inputAmount: string; + inputAmountUSD: string; outputAmount: string; + outputAmountUSD: string; datetime: { date: string; time: string; @@ -139,17 +160,22 @@ type TableItem = { components: { BaseWidget: lazyComponent(Components.BaseWidget), LinksDropdown: lazyComponent(Components.LinksDropdown), + TokenSelectButton: lazyComponent(Components.TokenSelectButton), + SelectToken: lazyComponent(Components.SelectToken), TokenLogo: components.TokenLogo, FormattedAmount: components.FormattedAmount, + FormattedAmountWithFiatValue: components.FormattedAmountWithFiatValue, FormattedAddress: components.FormattedAddress, HistoryPagination: components.HistoryPagination, }, }) -export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, IndexerDataFetchMixin) { +export default class SwapTransactionsWidget extends Mixins( + WithTokenSelectMixin, + ScrollableTableMixin, + IndexerDataFetchMixin +) { @state.wallet.settings.soraNetwork private soraNetwork!: Nullable; - @getter.swap.tokenFrom tokenFrom!: Nullable; - @getter.swap.tokenTo tokenTo!: Nullable; @getter.wallet.account.assetsDataTable private assetsDataTable!: WALLET_TYPES.AssetsTable; @Watch('assetsAddresses', { immediate: true }) @@ -157,14 +183,14 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, this.checkTriggerUpdate(curr, prev); } - pageAmount = 5; // override PaginationSearchMixin + fetchAmount = 100; // override IndexerDataFetchMixin private readonly operations = [Operation.Swap]; - private readonly fromTimestamp = dayjs().subtract(1, 'month').startOf('day').unix(); // month ago, start of the day + private readonly fromTimestamp = dayjs().subtract(1, 'week').startOf('day').unix(); // week ago, start of the day // override ScrollableTableMixin get tableItems(): TableItem[] { - return this.items.map((item) => { + return this.visibleItems.map((item) => { const txId = item.id ?? ''; const blockId = item.blockId ?? ''; const address = item.from ?? ''; @@ -173,7 +199,9 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, const outputAsset = item.asset2Address ? this.assetsDataTable[item.asset2Address] : null; const outputAssetSymbol = outputAsset?.symbol ?? '??'; const inputAmount = showMostFittingValue(new FPNumber(item.amount ?? 0)); + const inputAmountUSD = new FPNumber(item.payload.amountUSD ?? 0).toLocaleString(); const outputAmount = showMostFittingValue(new FPNumber(item.amount2 ?? 0)); + const outputAmountUSD = new FPNumber(item.payload.amount2USD ?? 0).toLocaleString(); const date = dayjs(item.startTime); const links = soraExplorerLinks(this.soraNetwork, txId, blockId); @@ -184,7 +212,9 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, outputAsset, outputAssetSymbol, inputAmount, + inputAmountUSD, outputAmount, + outputAmountUSD, datetime: { date: date.format('M/DD'), time: date.format('HH:mm:ss') }, links, }; @@ -192,7 +222,7 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, } get assetsAddresses(): string[] { - const filtered = [this.tokenFrom, this.tokenTo].filter((token) => !!token) as AccountAsset[]; + const filtered = [this.selectedToken].filter((token) => !!token) as AccountAsset[]; return filtered .map((token) => token.address) @@ -205,14 +235,12 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, private createFilter(timestamp?: number) { const indexer = getCurrentIndexer(); - const { operations, tokenFrom, tokenTo } = this; - const assetAddress = tokenFrom?.address; - const assetsAddresses = tokenTo?.address ? [tokenTo.address] : []; + const { operations, selectedToken } = this; + const assetAddress = selectedToken?.address; const filter = indexer.historyElementsFilter({ operations, assetAddress, timestamp, - query: { assetsAddresses }, }); return filter; @@ -222,8 +250,8 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin, get dataVariables(): FetchVariables { return { filter: this.createFilter(this.fromTimestamp), - first: this.pageAmount, - offset: this.pageAmount * (this.currentPage - 1), + first: this.fetchAmount, + offset: this.fetchAmount * (this.fetchPage - 1), }; } @@ -269,6 +297,16 @@ export default class SwapTransactionsWidget extends Mixins(ScrollableTableMixin,