Skip to content

Commit

Permalink
Merge pull request #738 from rainlanguage/2024-07-24-vault-detail
Browse files Browse the repository at this point in the history
splitting out vault detail
  • Loading branch information
hardyjosh authored Aug 5, 2024
2 parents 0849124 + f3394f9 commit 0f0ac7b
Show file tree
Hide file tree
Showing 15 changed files with 799 additions and 213 deletions.
2 changes: 1 addition & 1 deletion tauri-app/src/lib/components/CardProperty.svelte
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div>
<div {...$$props}>
<h5 class="text-md mb-1 w-full tracking-tight text-gray-400 dark:text-gray-400">
<slot name="key" />
</h5>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
function setData() {
if (series === undefined || data.length === 0) return;
series.setData(data);
setTimeScale();
}
Expand All @@ -96,7 +95,7 @@
}
$: timeDelta, setTimeScale();
$: data, setData();
$: data, series, setData();
$: $lightweightChartsTheme, setOptions();
onMount(() => {
Expand Down Expand Up @@ -153,11 +152,16 @@
</div>
<div class="relative flex w-full grow items-center justify-center bg-white dark:bg-gray-800">
{#if data.length === 0 && !loading}
<div class="text-gray-800 dark:text-gray-400">
<div class="text-gray-800 dark:text-gray-400" data-testid="lightweightChartEmptyMessage">
{emptyMessage}
</div>
{:else}
<div bind:this={chartElement} class="h-full w-full overflow-hidden" {...$$props}></div>
<div
bind:this={chartElement}
class="h-full w-full overflow-hidden"
{...$$props}
data-testid="lightweightChartElement"
></div>
{/if}
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script lang="ts" generics="T">
import LightweightChartLine from './LightweightChartLine.svelte';
import type { CreateQueryResult } from '@tanstack/svelte-query';
import type { UTCTimestamp } from 'lightweight-charts';
import { sortBy } from 'lodash';
// eslint-disable-next-line no-undef
export let query: CreateQueryResult<T[]>;
// eslint-disable-next-line no-undef
export let timeTransform: (data: T) => UTCTimestamp;
// eslint-disable-next-line no-undef
export let valueTransform: (data: T) => number;
// eslint-disable-next-line no-undef
const transformAndSortData = (data: T[]) => {
const transformedData = data.map((d) => ({
value: valueTransform(d),
time: timeTransform(d),
}));
return sortBy(transformedData, (d) => d.time);
};
$: data = transformAndSortData($query.data ?? []);
</script>

<LightweightChartLine {data} loading={$query.isLoading} {...$$props} />
31 changes: 31 additions & 0 deletions tauri-app/src/lib/components/charts/VaultBalanceChart.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<script lang="ts">
import { timestampSecondsToUTCTimestamp } from '$lib/utils/time';
import { bigintToFloat } from '$lib/utils/number';
import type { Vault } from '$lib/typeshare/vaultDetail';
import { createQuery } from '@tanstack/svelte-query';
import { vaultBalanceChangesList } from '$lib/queries/vaultBalanceChangesList';
import { subgraphUrl } from '$lib/stores/settings';
import TanstackLightweightChartLine from './TanstackLightweightChartLine.svelte';
export let vault: Vault;
$: query = createQuery({
queryKey: ['vaultBalanceChanges', vault.id],
queryFn: () => {
return vaultBalanceChangesList(vault.id, '', 0, 1000);
},
enabled: !!$subgraphUrl,
});
</script>

{#if vault}
<TanstackLightweightChartLine
title="Balance history"
priceSymbol={vault.token.symbol}
{query}
timeTransform={(d) => timestampSecondsToUTCTimestamp(BigInt(d.timestamp))}
valueTransform={(d) =>
bigintToFloat(BigInt(d.new_vault_balance), Number(vault.token.decimals ?? 0))}
emptyMessage="No deposits or withdrawals found"
/>
{/if}
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,28 @@
// eslint-disable-next-line no-undef
export let query: CreateQueryResult<T>;
export let emptyMessage = 'Not found';
// We need to explicitly define the data type as non-nullable here because
// doing it in the component body ({#if $query.data}) doesn't make the slot
// prop non-nullable when used in the parent component.
// eslint-disable-next-line no-undef
let data: NonNullable<T>;
$: if ($query.data) {
data = $query.data;
}
</script>

{#if $query.data}
{#if data}
<div class="mb-6 flex items-end justify-between">
<slot name="top" data={$query.data} />
<slot name="top" {data} />
</div>
<div class="grid grid-cols-3 gap-4">
<div class="col-span-1 flex flex-col gap-y-6">
<slot name="card" data={$query.data} />
<slot name="card" {data} />
</div>
<div class="col-span-2 min-h-[500px]">
<slot name="chart" data={$query.data} />
<slot name="chart" {data} />
</div>
</div>
<div class="w-full">
Expand Down
136 changes: 136 additions & 0 deletions tauri-app/src/lib/components/detail/VaultDetail.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<script lang="ts">
import VaultBalanceChangesTable from '../tables/VaultBalanceChangesTable.svelte';
import { Button } from 'flowbite-svelte';
import { walletAddressMatchesOrBlank } from '$lib/stores/wallets';
import { bigintStringToHex } from '$lib/utils/hex';
import Hash from '$lib/components/Hash.svelte';
import { HashType } from '$lib/types/hash';
import { goto } from '$app/navigation';
import { ArrowDownOutline, ArrowUpOutline } from 'flowbite-svelte-icons';
import CardProperty from '$lib/components/CardProperty.svelte';
import { formatUnits } from 'viem';
import { createQuery } from '@tanstack/svelte-query';
import { vaultDetail } from '$lib/queries/vaultDetail';
import { QKEY_VAULT } from '$lib/queries/keys';
import { subgraphUrl } from '$lib/stores/settings';
import { handleDepositModal, handleWithdrawModal } from '$lib/services/modal';
import TanstackContentDetail from '$lib/components/detail/TanstackPageContentDetail.svelte';
import VaultBalanceChart from '$lib/components/charts/VaultBalanceChart.svelte';
export let id: string;
$: vaultDetailQuery = createQuery({
queryKey: [QKEY_VAULT + id],
queryFn: () => {
return vaultDetail(id, $subgraphUrl || '');
},
enabled: !!$subgraphUrl,
});
</script>

<TanstackContentDetail query={vaultDetailQuery} emptyMessage="Vault not found">
<svelte:fragment slot="top" let:data>
<div
data-testid="vaultDetailTokenName"
class="flex gap-x-4 text-3xl font-medium dark:text-white"
>
{data.token.name}
</div>
<div>
{#if $walletAddressMatchesOrBlank(data.owner)}
<Button
data-testid="vaultDetailDepositButton"
color="dark"
on:click={() => handleDepositModal(data)}
><ArrowDownOutline size="xs" class="mr-2" />Deposit</Button
>
<Button
data-testid="vaultDetailWithdrawButton"
color="dark"
on:click={() => handleWithdrawModal(data)}
><ArrowUpOutline size="xs" class="mr-2" />Withdraw</Button
>
{/if}
</div>
</svelte:fragment>
<svelte:fragment slot="card" let:data>
<CardProperty data-testid="vaultDetailVaultId">
<svelte:fragment slot="key">Vault ID</svelte:fragment>
<svelte:fragment slot="value">{bigintStringToHex(data.vault_id)}</svelte:fragment>
</CardProperty>

<CardProperty data-testid="vaultDetailOwnerAddress">
<svelte:fragment slot="key">Owner Address</svelte:fragment>
<svelte:fragment slot="value">
<Hash type={HashType.Wallet} value={data.owner} />
</svelte:fragment>
</CardProperty>

<CardProperty data-testid="vaultDetailTokenAddress">
<svelte:fragment slot="key">Token address</svelte:fragment>
<svelte:fragment slot="value">
<Hash value={data.token.id} />
</svelte:fragment>
</CardProperty>

<CardProperty data-testid="vaultDetailBalance">
<svelte:fragment slot="key">Balance</svelte:fragment>
<svelte:fragment slot="value"
>{formatUnits(BigInt(data.balance), Number(data.token.decimals ?? 0))}
{data.token.symbol}</svelte:fragment
>
</CardProperty>

<CardProperty>
<svelte:fragment slot="key">Orders as input</svelte:fragment>
<svelte:fragment slot="value">
<p data-testid="vaultDetailOrdersAsInput" class="flex flex-wrap justify-start">
{#if data.orders_as_input && data.orders_as_input.length > 0}
{#each data.orders_as_input as order}
<Button
class={'mr-1 mt-1 px-1 py-0' + (!order.active ? ' opacity-50' : '')}
color="light"
data-order={order.id}
data-testid={'vaultDetailOrderAsInputOrder' + order.id}
on:click={() => goto(`/orders/${order.id}`)}
>
<Hash type={HashType.Identifier} value={order.order_hash} copyOnClick={false} />
</Button>
{/each}
{:else}
None
{/if}
</p>
</svelte:fragment>
</CardProperty>

<CardProperty>
<svelte:fragment slot="key">Orders as output</svelte:fragment>
<svelte:fragment slot="value">
<p data-testid="vaulDetailOrdersAsOutput" class="flex flex-wrap justify-start">
{#if data.orders_as_output && data.orders_as_output.length > 0}
{#each data.orders_as_output as order}
<Button
class={'mr-1 mt-1 px-1 py-0' + (!order.active ? ' opacity-50' : '')}
color="alternative"
data-order={order.id}
data-testid={'vaultDetailOrderAsOutputOrder' + order.id}
on:click={() => goto(`/orders/${order.id}`)}
>
<Hash type={HashType.Identifier} value={order.order_hash} copyOnClick={false} />
</Button>
{/each}
{:else}
None
{/if}
</p>
</svelte:fragment>
</CardProperty>
</svelte:fragment>

<svelte:fragment slot="chart" let:data>
<VaultBalanceChart vault={data} />
</svelte:fragment>

<svelte:fragment slot="below"><VaultBalanceChangesTable {id} /></svelte:fragment>
</TanstackContentDetail>
Loading

0 comments on commit 0f0ac7b

Please sign in to comment.