Skip to content

Commit

Permalink
fix(app): save progress
Browse files Browse the repository at this point in the history
  • Loading branch information
Swepool committed Dec 12, 2024
1 parent ba81220 commit 96810ae
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 127 deletions.
38 changes: 19 additions & 19 deletions app/src/lib/components/TransferFrom/index.svelte
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<script lang="ts">
import { TRANSFER_DEBUG } from "$lib/components/TransferFrom/config.ts"
import { createTransferStore } from "$lib/components/TransferFrom/transfer"
import DebugBox from "$lib/components/TransferFrom/components/DebugBox.svelte"
import { TRANSFER_DEBUG } from "$lib/components/TransferFrom/config.ts"
import { createTransferStore } from "$lib/components/TransferFrom/transfer"
import DebugBox from "$lib/components/TransferFrom/components/DebugBox.svelte"
const { intents, context, validation } = createTransferStore()
const { intents, context, validation } = createTransferStore()
</script>

<form
Expand All @@ -21,12 +21,12 @@ const { intents, context, validation } = createTransferStore()
id="source"
name="source"
placeholder="Enter source chain"
class="w-[300px] p-1 {$validation.source ? 'border-red-500' : ''}"
class="w-[300px] p-1 {$validation.errors.source ? 'border-red-500' : ''}"
value={$intents.source}
on:input={event => intents.updateField('source', event)}
/>
{#if $validation.source}
<span class="text-red-500 text-sm">{$validation.source}</span>
{#if $validation.errors.source}
<span class="text-red-500 text-sm">{$validation.errors.source}</span>
{/if}
</div>

Expand All @@ -37,12 +37,12 @@ const { intents, context, validation } = createTransferStore()
id="destination"
name="destination"
placeholder="Enter destination chain"
class="w-[300px] p-1 {$validation.destination ? 'border-red-500' : ''}"
class="w-[300px] p-1 {$validation.errors.destination ? 'border-red-500' : ''}"
value={$intents.destination}
on:input={event => intents.updateField('destination', event)}
/>
{#if $validation.destination}
<span class="text-red-500 text-sm">{$validation.destination}</span>
{#if $validation.errors.destination}
<span class="text-red-500 text-sm">{$validation.errors.destination}</span>
{/if}
</div>

Expand All @@ -53,12 +53,12 @@ const { intents, context, validation } = createTransferStore()
id="asset"
name="asset"
placeholder="Enter asset"
class="w-[300px] p-1 {$validation.asset ? 'border-red-500' : ''}"
class="w-[300px] p-1 {$validation.errors.asset ? 'border-red-500' : ''}"
value={$intents.asset}
on:input={event => intents.updateField('asset', event)}
/>
{#if $validation.asset}
<span class="text-red-500 text-sm">{$validation.asset}</span>
{#if $validation.errors.asset}
<span class="text-red-500 text-sm">{$validation.errors.asset}</span>
{/if}
</div>

Expand All @@ -80,12 +80,12 @@ const { intents, context, validation } = createTransferStore()
data-field="amount"
autocapitalize="none"
pattern="^[0-9]*[.,]?[0-9]*$"
class="w-[300px] p-1 {$validation.amount ? 'border-red-500' : ''}"
class="w-[300px] p-1 {$validation.errors.amount ? 'border-red-500' : ''}"
value={$intents.amount}
on:input={event => intents.updateField('amount', event)}
/>
{#if $validation.amount}
<span class="text-red-500 text-sm">{$validation.amount}</span>
{#if $validation.errors.amount}
<span class="text-red-500 text-sm">{$validation.errors.amount}</span>
{/if}
</div>

Expand All @@ -101,13 +101,13 @@ const { intents, context, validation } = createTransferStore()
spellcheck="false"
autocomplete="off"
data-field="receiver"
class="w-[300px] p-1 disabled:bg-black/30 {$validation.receiver ? 'border-red-500' : ''}"
class="w-[300px] p-1 disabled:bg-black/30 {$validation.errors.receiver ? 'border-red-500' : ''}"
placeholder="Enter destination address"
value={$intents.receiver}
on:input={event => intents.updateField('receiver', event)}
/>
{#if $validation.receiver}
<span class="text-red-500 text-sm">{$validation.receiver}</span>
{#if $validation.errors.receiver}
<span class="text-red-500 text-sm">{$validation.errors.receiver}</span>
{/if}
</div>
</div>
Expand Down
66 changes: 34 additions & 32 deletions app/src/lib/components/TransferFrom/transfer/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export type AddressBalance = {
gasToken: boolean
address: Address
symbol: string
chain_id: string
}

export type NamedBalance = {
Expand All @@ -21,22 +22,25 @@ export type NamedBalance = {
name: string | null
symbol: string
gasToken: boolean
chain_id: string
}

export type EmptyBalance = {}
export type EmptyBalance = {
chain_id: string
}

export type Balance = AddressBalance | NamedBalance | EmptyBalance

export interface ContextStore {
chains: Array<Chain>
userAddress: Readable<UserAddresses>
sourceChain: Readable<Chain | undefined>
destinationChain: Readable<Chain | undefined>
balances: Readable<Array<Balance>>
assetInfo: Readable<Balance | undefined>
userAddress: UserAddresses
sourceChain: Chain | undefined
destinationChain: Chain | undefined
balances: Array<Balance>
assetInfo: Balance | undefined
}

export function createContextStore(intents: IntentStore): ContextStore {
export function createContextStore(intents: IntentStore): Readable<ContextStore> {
const queryClient = useQueryClient()

function queryData<T extends Array<unknown>>(
Expand Down Expand Up @@ -65,42 +69,40 @@ export function createContextStore(intents: IntentStore): ContextStore {
chains.find(chain => chain.chain_id === intentsValue.destination)
)

// Balance data
const balances = derived(
[
intents,
userAddress,
userBalancesQuery({ chains, connected: true, userAddr: get(userAddress) })
],
([intentsValue, _userAddressValue, rawBalances]) => {
const sourceChain = chains.find(chain => chain.chain_id === intentsValue.source)
if (!sourceChain) return []

const chainIndex = chains.findIndex(c => c.chain_id === sourceChain.chain_id)
const balanceResult = rawBalances[chainIndex]

if (!balanceResult?.isSuccess || balanceResult.data instanceof Error) {
console.log("No balances fetched yet for selected chain")
return []
}

return balanceResult.data.map(balance => ({
...balance,
balance: BigInt(balance.balance)
}))
([_intentsValue, _userAddressValue, rawBalances]) => {
return rawBalances.flatMap((balanceResult, index) => {
const chain = chains[index]
if (!balanceResult?.isSuccess || balanceResult.data instanceof Error) {
return []
}
return balanceResult.data.map(balance => ({
...balance,
balance: BigInt(balance.balance),
chain_id: chain.chain_id // Add chain_id to each balance
}))
})
}
)

const assetInfo = derived([balances, intents], ([balancesValue, intentsValue]) =>
balancesValue.find(x => x?.address === intentsValue.asset)
)

return {
chains,
userAddress,
sourceChain,
destinationChain,
balances,
assetInfo
}
return derived(
[userAddress, sourceChain, destinationChain, balances, assetInfo],
([$userAddress, $sourceChain, $destinationChain, $balances, $assetInfo]) => ({
chains,
userAddress: $userAddress,
sourceChain: $sourceChain,
destinationChain: $destinationChain,
balances: $balances,
assetInfo: $assetInfo
})
)
}
21 changes: 6 additions & 15 deletions app/src/lib/components/TransferFrom/transfer/index.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,14 @@
import { type Readable } from "svelte/store"
import { createIntentStore, type IntentStore } from "./intents.ts"
import type { Readable } from "svelte/store"
import type { Chain, UserAddresses } from "$lib/types.ts"
import { type Balance, createContextStore } from "$lib/components/TransferFrom/transfer/context.ts"
import { type ContextStore, createContextStore} from "$lib/components/TransferFrom/transfer/context.ts"
import {
createValidationStore,
type ValidationStore
createValidationStore, type ValidationStoreAndMethods
} from "$lib/components/TransferFrom/transfer/validation.ts"

export interface TransferStore {
intents: IntentStore
context: {
chains: Array<Chain>
userAddress: Readable<UserAddresses>
sourceChain: Readable<Chain | undefined>
destinationChain: Readable<Chain | undefined>
balances: Readable<Array<Balance>>
assetInfo: Readable<Balance | undefined>
}
validation: ValidationStore
context: Readable<ContextStore>
validation: ValidationStoreAndMethods
}

export function createTransferStore(): TransferStore {
Expand All @@ -30,4 +21,4 @@ export function createTransferStore(): TransferStore {
context,
validation
}
}
}
97 changes: 36 additions & 61 deletions app/src/lib/components/TransferFrom/transfer/validation.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import type {Readable} from "svelte/store"
import {derived} from "svelte/store"
import type {IntentStore, FormFields, RawTransferIntents} from "./intents.ts"
import type {Chain} from "$lib/types"
import type {Balance, ContextStore} from "$lib/components/TransferFrom/transfer/context"
import {transferSchema} from "./schema.ts"
import {safeParse} from "valibot"
import type { Readable } from "svelte/store"
import { derived } from "svelte/store"
import type { IntentStore, FormFields, RawTransferIntents } from "./intents.ts"
import type { Chain } from "$lib/types"
import type { Balance, ContextStore } from "$lib/components/TransferFrom/transfer/context"
import { transferSchema } from "./schema.ts"
import { safeParse } from "valibot"

export type FieldErrors = Partial<Record<keyof FormFields, string>>

export interface ValidationStore extends Readable<FieldErrors> {
export interface ValidationStore {
errors: FieldErrors
isValid: boolean
}

export interface ValidationStoreAndMethods extends Readable<ValidationStore> {
validate: () => Promise<boolean>
}

Expand All @@ -22,34 +27,30 @@ interface ValidationContext {

export function createValidationStore(
intents: IntentStore,
context: ContextStore
): ValidationStore {
const errors = derived<
[
Readable<RawTransferIntents>,
Readable<Array<Balance>>,
Readable<Chain | undefined>,
Readable<Chain | undefined>,
Readable<Balance | undefined>
],
FieldErrors
>(
[intents, context.balances, context.sourceChain, context.destinationChain, context.assetInfo],
([$intents, $balances, $sourceChain, $destinationChain, $assetInfo]) => {
return validateAll({
context: Readable<ContextStore>
): ValidationStoreAndMethods {
const store = derived(
[intents, context],
([$intents, $context]) => {
const errors = validateAll({
formFields: {
source: $intents.source,
destination: $intents.destination,
asset: $intents.asset,
receiver: $intents.receiver,
amount: $intents.amount
},
balances: $balances,
sourceChain: $sourceChain,
destinationChain: $destinationChain,
assetInfo: $assetInfo,
chains: context.chains
balances: $context.balances,
sourceChain: $context.sourceChain,
destinationChain: $context.destinationChain,
assetInfo: $context.assetInfo,
chains: $context.chains
})

return {
errors,
isValid: Object.keys(errors).length === 0
}
}
)

Expand Down Expand Up @@ -104,37 +105,12 @@ export function createValidationStore(
})
}

function validateSchema(params: FormFields): FieldErrors {
if (Object.values(params).every(value => !value)) {
return {}
}

const result = safeParse(transferSchema, params)

if (!result.success) {
return result.issues.reduce((acc, issue) => {
const fieldName = issue.path?.[0]?.key as keyof FormFields

if (fieldName && !params[fieldName]) {
return acc
}

if (fieldName) {
acc[fieldName] = issue.message
}
return acc
}, {} as FieldErrors)
}

return {}
}

function validateBusinessRules(formFields: FormFields, context: ValidationContext): FieldErrors {
if (Object.values(formFields).every(value => !value)) {
return {}
}
const errors: FieldErrors = {}

if (formFields.source && formFields.destination && formFields.source === formFields.destination) {
errors.destination = "Source and destination chains must be different"
}
Expand All @@ -143,18 +119,17 @@ export function createValidationStore(
}

return {
subscribe: errors.subscribe,
subscribe: store.subscribe,
validate: () => {
return new Promise(resolve => {
let currentErrors: FieldErrors = {}
const unsubscribe = errors.subscribe(value => {
currentErrors = value
let currentState: ValidationStore
const unsubscribe = store.subscribe(value => {
currentState = value
})

const isValid = Object.keys(currentErrors).length === 0
const isValid = currentState!.isValid
unsubscribe()
resolve(isValid)
})
}
}
}
}

0 comments on commit 96810ae

Please sign in to comment.