-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: adds MVP exchange screen in buy/sell view
- Loading branch information
1 parent
746c846
commit 6a8f492
Showing
28 changed files
with
526 additions
and
231 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
50 changes: 50 additions & 0 deletions
50
packages/desktop/views/dashboard/buy-sell/components/TokenTile.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<script lang="ts"> | ||
import { formatCurrency } from '@core/i18n/utils' | ||
import { getMarketPriceForToken, getTokenValueFromFiatAmount } from '@core/market/actions' | ||
import { formatTokenAmount } from '@core/token' | ||
import { truncateString } from '@core/utils' | ||
import { Tile, Text } from '@bloomwalletio/ui' | ||
import { TokenAvatar } from '@ui' | ||
import { selectedAccountTokens } from '@core/token/stores' | ||
import { SupportedStardustNetworkId } from '@core/network' | ||
import { MarketCurrency } from '@core/market' | ||
export let token = $selectedAccountTokens[SupportedStardustNetworkId.Iota]?.baseCoin | ||
export let onClick: (() => unknown) | undefined = undefined | ||
export let selected = false | ||
export let fiatValue = '0' | ||
export let currency: MarketCurrency | ||
$: marketPrice = token ? getMarketPriceForToken(token, currency) : undefined | ||
$: tokenAmount = token && currency ? getTokenValueFromFiatAmount(fiatValue, token, currency) : BigInt(0) | ||
</script> | ||
|
||
{#if token && token.metadata} | ||
<Tile {onClick} {selected} surface={1} width="full"> | ||
<div class="w-full flex flex-row items-center gap-2"> | ||
<TokenAvatar {token} /> | ||
<div class="flex flex-col w-full"> | ||
<div class="flex flex-row w-full justify-between"> | ||
<Text> | ||
{token.metadata.name | ||
? truncateString(token.metadata.name, 13, 0) | ||
: truncateString(token.id, 6, 7)} | ||
</Text> | ||
<Text align="right"> | ||
{token.metadata | ||
? `≈ ${formatTokenAmount(tokenAmount ?? BigInt(0), token.metadata, { round: false })}` | ||
: '-'} | ||
</Text> | ||
</div> | ||
<div class="flex flex-row w-full justify-between"> | ||
<Text fontWeight="medium" textColor="secondary"> | ||
{marketPrice ? formatCurrency(marketPrice, currency) : '-'} | ||
</Text> | ||
<Text fontWeight="medium" textColor="secondary" align="right"> | ||
{fiatValue !== undefined ? formatCurrency(fiatValue ?? '', currency) : '-'} | ||
</Text> | ||
</div> | ||
</div> | ||
</div> | ||
</Tile> | ||
{/if} |
49 changes: 0 additions & 49 deletions
49
packages/desktop/views/dashboard/buy-sell/components/TransakAccountPanel.svelte
This file was deleted.
Oops, something went wrong.
26 changes: 26 additions & 0 deletions
26
packages/desktop/views/dashboard/buy-sell/components/TransakAmountInput.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
<script lang="ts"> | ||
import { activeProfile } from '@core/profile/stores' | ||
import { AmountInput } from '@ui' | ||
import { Text } from '@bloomwalletio/ui' | ||
export let currency = $activeProfile?.settings?.marketCurrency?.toUpperCase() | ||
export let value: string | ||
let inputLength: number | ||
const fontSize: 'text-32' | 'text-48' | 'text-64' = 'text-64' | ||
$: value, (inputLength = getInputLength()) | ||
$: maxWidth = `${(inputLength * Number(/\d+/.exec(fontSize)?.[0] ?? 0) * 2) / 3}px` | ||
function getInputLength(): number { | ||
const length = value?.length || 1 | ||
const isDecimal = value?.includes('.') || value?.includes(',') | ||
return length - (isDecimal ? 0.5 : 0) | ||
} | ||
</script> | ||
|
||
<div class="flex justify-center items-end gap-1"> | ||
<AmountInput bind:value maxDecimals={2} {fontSize} {maxWidth} /> | ||
<Text class="pb-5">{currency}</Text> | ||
</div> |
198 changes: 198 additions & 0 deletions
198
packages/desktop/views/dashboard/buy-sell/components/TransakExchangePanel.svelte
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
<script lang="ts"> | ||
import { Button, Icon, IconName, IOption, SelectInput, Tabs, Text } from '@bloomwalletio/ui' | ||
import { DISCORD_URL } from '@contexts/settings/constants' | ||
import { ISettingsState, settingsState } from '@contexts/settings/stores' | ||
import { selectedAccount, selectedAccountIndex } from '@core/account/stores' | ||
import { openUrlInBrowser, Platform } from '@core/app' | ||
import { localize } from '@core/i18n' | ||
import { FiatCurrency } from '@core/market' | ||
import { MarketCoinId } from '@core/market/enums' | ||
import { marketCoinPrices } from '@core/market/stores' | ||
import { isDashboardSideBarExpanded } from '@core/ui' | ||
import { drawerState } from '@desktop/auxiliary/drawer/stores' | ||
import { DrawerState } from '@desktop/auxiliary/drawer/types' | ||
import { IPopupState, IProfileAuthPopupState, popupState, profileAuthPopup } from '@desktop/auxiliary/popup' | ||
import { isFeatureEnabled } from '@lib/features/utils' | ||
import { Pane } from '@ui' | ||
import { onDestroy, tick } from 'svelte' | ||
import { TokenTile, TransakAmountInput } from './' | ||
import { transakFiatCurrencies } from '@auxiliary/transak' | ||
const CURRENCY_OPTIONS: IOption[] = Object.keys(FiatCurrency).map((currency) => ({ | ||
value: currency, | ||
})) | ||
let selectedCurrencyOption: IOption = CURRENCY_OPTIONS[0] | ||
let error: boolean = false | ||
$: fiatValue = String(getDefaultFiatAmount(FiatCurrency[selectedCurrencyOption.value as keyof typeof FiatCurrency])) | ||
Platform.onEvent('transak-loaded', () => (isTransakLoading = false)) | ||
Platform.onEvent('transak-not-loaded', () => (error = true)) | ||
function onButtonClick(): void { | ||
openUrlInBrowser(DISCORD_URL) | ||
} | ||
const TABS = [ | ||
{ key: 'BUY', value: localize('views.buySell.tabs.buy') }, | ||
{ key: 'SELL', value: localize('views.buySell.tabs.sell') }, | ||
] | ||
let selectedTab = TABS[0] | ||
$: paymentOptions = | ||
$transakFiatCurrencies?.[selectedCurrency as keyof typeof FiatCurrency]?.paymentOptions.map((option) => ({ | ||
value: option.id, | ||
label: option.name, | ||
})) ?? [] | ||
$: selectedPaymentOption = paymentOptions?.[0] | ||
$: selectedCurrency = selectedCurrencyOption.value | ||
let isTransakOpen: boolean = false | ||
let isTransakLoading: boolean = false | ||
$: $isDashboardSideBarExpanded, void updateTransakBounds() | ||
$: if ($selectedAccountIndex !== undefined) { | ||
void closeTransak() | ||
} | ||
$: isTransakOpen, void handleOverlayChanges($popupState, $profileAuthPopup, $settingsState, $drawerState) | ||
async function handleOverlayChanges( | ||
state: IPopupState, | ||
profilePopupState: IProfileAuthPopupState, | ||
settingsState: ISettingsState, | ||
drawerState: DrawerState | ||
): Promise<void> { | ||
if (state.active || profilePopupState.active || settingsState.open || drawerState?.id) { | ||
await Platform.hideTransak() | ||
} else { | ||
await tick() | ||
await Platform.showTransak() | ||
} | ||
} | ||
let transakContainer: HTMLDivElement | undefined | ||
async function updateTransakBounds(): Promise<void> { | ||
if (!transakContainer) { | ||
return | ||
} | ||
const rect = transakContainer.getBoundingClientRect() | ||
const transakPaneStyles = getComputedStyle(transakContainer?.children[0]) | ||
const extractDigitsToNumbers = (str: string) => Number(str?.replace(/\D/g, '') ?? 0) | ||
const borderTop = extractDigitsToNumbers(transakPaneStyles?.borderTopWidth) | ||
const borderBottom = extractDigitsToNumbers(transakPaneStyles?.borderBottomWidth) | ||
const borderLeft = extractDigitsToNumbers(transakPaneStyles?.borderLeftWidth) | ||
const borderRight = extractDigitsToNumbers(transakPaneStyles?.borderRightWidth) | ||
await Platform.updateTransakBounds({ | ||
x: rect.x + borderLeft, | ||
y: rect.y + borderTop, | ||
width: rect.width - borderLeft - borderRight, | ||
height: rect.height - borderTop - borderBottom, | ||
}) | ||
} | ||
function getDefaultFiatAmount(currency: FiatCurrency): number { | ||
const DEFAULT_FIAT_AMOUNT = 100 | ||
switch (currency) { | ||
case FiatCurrency.USD: | ||
case FiatCurrency.EUR: | ||
case FiatCurrency.GBP: | ||
return DEFAULT_FIAT_AMOUNT | ||
default: { | ||
const conversionRate = | ||
$marketCoinPrices[MarketCoinId.Iota]?.[currency] / | ||
$marketCoinPrices[MarketCoinId.Iota]?.[FiatCurrency.USD] | ||
const fiatAmount = DEFAULT_FIAT_AMOUNT * conversionRate | ||
const roundedAmount = customRound(fiatAmount) | ||
return roundedAmount | ||
} | ||
} | ||
} | ||
function customRound(number) { | ||
const magnitude = Math.pow(10, Math.floor(Math.log10(number))) | ||
return magnitude <= 10 | ||
? Math.round(number / magnitude) * magnitude | ||
: Math.round((number / magnitude) * 10) * (magnitude / 10) | ||
} | ||
export async function resetTransak(): Promise<void> { | ||
isTransakLoading = true | ||
await Platform.closeTransak() | ||
isTransakOpen = false | ||
await Platform.openTransak({ | ||
currency: selectedCurrencyOption.value as keyof typeof FiatCurrency, | ||
address: $selectedAccount?.depositAddress ?? '', | ||
service: selectedTab.key as 'BUY' | 'SELL', | ||
amount: Number(fiatValue), | ||
paymentMethod: selectedPaymentOption.value ?? '', | ||
}) | ||
isTransakOpen = true | ||
await updateTransakBounds() | ||
} | ||
async function closeTransak(): Promise<void> { | ||
await Platform.closeTransak() | ||
isTransakOpen = false | ||
} | ||
onDestroy(() => { | ||
void Platform.closeTransak() | ||
isTransakOpen = false | ||
Platform.removeListenersForEvent('reset-transak') | ||
}) | ||
</script> | ||
|
||
<svelte:window on:resize={updateTransakBounds} /> | ||
|
||
<div class="w-full h-full" bind:this={transakContainer}> | ||
<Pane classes="px-6 pb-6 pt-4 bg-surface dark:bg-surface-dark shadow-lg w-full h-full"> | ||
{#if error} | ||
<div class="flex flex-col justify-center items-center w-full h-full gap-4"> | ||
<Icon name={IconName.ArrowDownUp} size="lg" textColor="brand" /> | ||
<Text type="body1">{localize('views.buySell.error.title')}</Text> | ||
<Text textColor="secondary" align="center">{localize('views.buySell.error.description')}</Text> | ||
<Button on:click={onButtonClick} text={localize('actions.visitDiscord')} /> | ||
</div> | ||
{:else} | ||
<div | ||
class="flex flex-col justify-between items-center w-full h-full gap-8 {isTransakOpen && | ||
!isTransakLoading | ||
? 'opacity-0 pointer-events-none' | ||
: ''}" | ||
> | ||
{#if isFeatureEnabled('buySell.sell')} | ||
<div class="w-full"> | ||
<Tabs bind:selectedTab tabs={TABS} /> | ||
</div> | ||
{/if} | ||
<div class="flex flex-col items-center gap-4 w-full"> | ||
<div class="w-3/4"> | ||
<SelectInput | ||
label="Currency" | ||
options={CURRENCY_OPTIONS} | ||
bind:selected={selectedCurrencyOption} | ||
/> | ||
</div> | ||
<TransakAmountInput currency={selectedCurrency} bind:value={fiatValue} /> | ||
<TokenTile {fiatValue} currency={FiatCurrency[selectedCurrency]} /> | ||
<div class="w-full"> | ||
<SelectInput | ||
label="Payment method" | ||
options={paymentOptions} | ||
bind:selected={selectedPaymentOption} | ||
hideValue | ||
/> | ||
</div> | ||
</div> | ||
<Button text={selectedTab.value} on:click={resetTransak} /> | ||
</div> | ||
{/if} | ||
</Pane> | ||
</div> |
Oops, something went wrong.