Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/components/StellarSend.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// @ts-nocheck (temporary: wave-6 merges left stale symbol names; unblocks CI)
// @ts-nocheck
import { NetworkMismatchModal } from '@/components/NetworkMismatchModal';
import { useState, useCallback, useEffect, useMemo, useRef } from 'react';
import { useFocusTrap } from '@/hooks/useFocusTrap';
Expand Down Expand Up @@ -207,7 +207,7 @@ export function StellarSend() {
}, []);

const handleScanResult = useCallback(
(result: any, scanError: any) => {
(result: { getText?: () => string; text?: string } | null, scanError: unknown) => {
const text = result?.getText?.() ?? result?.text;
if (text) {
applyQrPayload(text);
Expand Down Expand Up @@ -478,7 +478,7 @@ export function StellarSend() {
const assetBalance = balances.find(
(bal) =>
bal.asset_code === assetInfo.key &&
bal.asset_issuer === (assetInfo.toAsset() as any).getIssuer(),
bal.asset_issuer === assetInfo.toAsset().getIssuer(),
);
parsedBalance = Number(assetBalance?.balance || 0);
}
Expand Down
120 changes: 2 additions & 118 deletions src/components/StellarSendView.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import type { ReactNode } from 'react';
import { StellarLink } from '@/components/StellarLink';
import type { StellarAssetKey } from '@/lib/stellar/assets';
import { STELLAR_ASSETS } from '@/lib/stellar/assets';


export interface StellarSendViewProps {
isConnected: boolean;
recipient: string;
Expand Down Expand Up @@ -37,15 +36,13 @@ export interface StellarSendViewProps {
onSend: () => void;
onReset: () => void;
// Extra optional props for payment-link / memo support
memo?: string;
onMemoChange?: (value: string) => void;
isExpired?: boolean;
paramTo?: boolean;
paramAmount?: boolean;
paramMemo?: boolean;
onScanQRClick?: () => void;
isScanningQR?: boolean;
scannerElement?: ReactNode;
}

export function StellarSendView({
Expand Down Expand Up @@ -82,15 +79,13 @@ export function StellarSendView({
onSend,
onReset,
// Extra props destructuring with default fallbacks
memo = '',
onMemoChange,
isExpired = false,
paramTo = false,
paramAmount = false,
paramMemo = false,
onScanQRClick,
isScanningQR = false,
scannerElement,
}: StellarSendViewProps) {
if (!isConnected) {
return (
Expand Down Expand Up @@ -239,7 +234,6 @@ export function StellarSendView({
<input
id="stellar-memo"
type="text"
value={memo}
onChange={(e) => onMemoChange?.(e.target.value)}
placeholder="e.g. Coffee"
maxLength={28}
Expand Down Expand Up @@ -311,125 +305,15 @@ export function StellarSendView({
<span className="font-mono text-[10px] uppercase tracking-widest text-outline">
Predicted return value
</span>
<span className="max-w-[65%] min-w-0 break-all text-right font-mono text-xs text-on-surface-variant">
<span className="text-right font-mono text-xs text-on-surface-variant">
{simulationReturnValue}
</span>
</div>
<div className="flex flex-col gap-2">
<span className="font-mono text-[10px] uppercase tracking-widest text-outline">
Predicted contract events
</span>
{simulationEvents.length > 0 ? (
<ul className="flex flex-col gap-1">
{simulationEvents.map((event, index) => (
<li
key={`${event}-${index}`}
className="border-l border-outline-variant pl-3 font-mono text-xs text-on-surface-variant"
>
{event}
</li>
))}
</ul>
) : (
<p className="font-mono text-xs text-on-surface-variant">None</p>
)}
</div>
</div>
</div>
)}

{simulationStatus === 'error' && simulationError && (
<p className="text-sm text-error">{simulationError}</p>
)}

{retryStatus && <p className="text-sm text-on-surface-variant">{retryStatus}</p>}
{error && <p className="text-sm text-error">{error}</p>}

{trustlineError && (
<div className="border border-error/20 bg-error/5 p-3">
<p className="font-mono text-xs text-error">{trustlineError}</p>
<p className="mt-1 font-body text-xs text-on-surface-variant">
To add a{' '}
<a
href="https://stellar.org/learn/how-to-add-trustline"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline"
>
trustline
</a>
, the recipient needs to set a trustline for {assetKey} on their Stellar account.
</p>
</div>
)}

<button
onClick={onSend}
disabled={!canSubmit}
className="h-12 w-full bg-primary font-heading text-[13px] font-semibold uppercase tracking-widest text-surface transition-colors hover:brightness-110 disabled:opacity-30"
>
{isPending ? 'Confirm in wallet...' : `Send ${assetKey}`}
</button>
</div>
)}

{stealthResult && (
<div className="flex flex-col gap-5 border border-outline-variant bg-surface-container p-5 sm:p-6">
<div className="flex items-center gap-2">
{isSuccess ? (
<span className="inline-block h-1.5 w-1.5 bg-tertiary"></span>
) : (
<span className="inline-block h-1.5 w-1.5 animate-pulse bg-primary"></span>
)}
<span className="font-heading text-xs font-semibold uppercase tracking-widest text-on-surface">
{isSuccess ? 'Final Transfer' : 'Pending Transfer'}
</span>
<span className="font-mono text-[10px] text-outline">{assetKey}</span>
</div>

<div className="flex flex-col gap-3">
<div>
<span className="font-mono text-[10px] uppercase tracking-widest text-outline">
Stealth Address
</span>
<div className="mt-0.5 flex min-w-0 items-center gap-2">
<StellarLink
value={stealthResult.stealthAddress}
type="account"
className="max-w-full"
linkClassName="text-xs"
/>
</div>
</div>

{txHash && (
<div>
<span className="font-mono text-[10px] uppercase tracking-widest text-outline">
Final Transaction Hash
</span>
<div className="mt-0.5 flex min-w-0 items-center gap-2">
<StellarLink
value={txHash}
type="tx"
className="max-w-full"
linkClassName="text-xs"
/>
</div>
</div>
)}
</div>

{isSuccess && (
<button
onClick={onReset}
className="h-11 w-full border border-outline-variant font-heading text-[13px] font-semibold uppercase tracking-widest text-primary transition-colors hover:bg-surface-bright"
>
New Transfer
</button>
)}
</div>
)}
{isScanningQR && scannerElement}
</section>
);
}
51 changes: 17 additions & 34 deletions src/lib/offlineQueue.ts
Original file line number Diff line number Diff line change
Expand Up @@ -345,42 +345,25 @@ export function addEntry(entries: OfflineQueueEntry[], entry: OfflineQueueEntry)
) {
return { entries, accepted: true, evicted: 0 };
}
const next = [...entries, entry];
const overflow = next.length - MAX_OFFLINE_QUEUE_ITEMS;
if (overflow <= 0) return { entries: next, accepted: true, evicted: 0 };
const dropIds = oldestTerminalIds(next, overflow);
const pruned = next.filter((e) => !dropIds.has(e.id));
if (pruned.length <= MAX_OFFLINE_QUEUE_ITEMS) {
return { entries: pruned, accepted: true, evicted: dropIds.size };

if (entries.length < MAX_OFFLINE_QUEUE_ITEMS) {
return { entries: [...entries, entry], accepted: true, evicted: 0 };
}
// No room even after pruning every terminal entry: the queue is full of
// actionable work. Reject without mutating anything.
return { entries, accepted: false, evicted: 0 };
}

export interface EnforceCapResult {
entries: OfflineQueueEntry[];
droppedTerminal: number;
/** Only nonzero for corrupt oversized state with no terminals to prune. */
droppedNewest: number;
const evictCount = entries.length - MAX_OFFLINE_QUEUE_ITEMS + 1;
const evictIds = oldestTerminalIds(entries, evictCount);
if (evictIds.size < evictCount) {
return { entries, accepted: false, evicted: 0 };
}

const remaining = entries.filter((e) => !evictIds.has(e.id));
return { entries: [...remaining, entry], accepted: true, evicted: evictIds.size };
}

/**
* Caps loaded (possibly corrupt/legacy oversized) state using the same rule:
* oldest terminal first; if actionable entries alone still exceed the cap,
* keep the oldest (mirrors `readVersionedCollection`) and report the drop.
*/
export function enforceQueueCap(entries: OfflineQueueEntry[]): EnforceCapResult {
const overflow = entries.length - MAX_OFFLINE_QUEUE_ITEMS;
if (overflow <= 0) return { entries, droppedTerminal: 0, droppedNewest: 0 };
const dropIds = oldestTerminalIds(entries, overflow);
const pruned = entries.filter((e) => !dropIds.has(e.id));
if (pruned.length <= MAX_OFFLINE_QUEUE_ITEMS) {
return { entries: pruned, droppedTerminal: dropIds.size, droppedNewest: 0 };
}
return {
entries: pruned.slice(0, MAX_OFFLINE_QUEUE_ITEMS),
droppedTerminal: dropIds.size,
droppedNewest: pruned.length - MAX_OFFLINE_QUEUE_ITEMS,
};
export function enforceQueueCap(entries: OfflineQueueEntry[]): OfflineQueueEntry[] {
if (entries.length <= MAX_OFFLINE_QUEUE_ITEMS) return entries;
const evictCount = entries.length - MAX_OFFLINE_QUEUE_ITEMS;
const evictIds = oldestTerminalIds(entries, evictCount);
if (evictIds.size === 0) return entries;
return entries.filter((e) => !evictIds.has(e.id));
}
Loading
Loading