feat(uploads/downloads): live progress bar driven by ant-core events - #65
Merged
Merged
Conversation
… the UI Surface real per-chunk progress for the direct-key wallet upload path (`wallet_upload`) and for both download flows. The plumbing is opt-in and keyed by transfer_id so events route to the right row. Rust (src-tauri/src/autonomi_ops.rs): - New mpsc forwarders that drain UploadEvent / DownloadEvent and re-emit them as Tauri `upload-progress` / `download-progress` events. - Quoting maps to the first 0-50% of the bar, storage to 50-100%, so the user sees continuous forward motion across both phases. - wallet_upload swaps `file_upload` for `file_upload_with_progress`. - download_file / download_public take a transfer_id arg and call `file_download_with_progress`. Frontend: - stores/files.ts gains TransferStage + stage/stageDone/stageTotal on FileEntry; setupProgressListeners() routes Tauri events back to the matching row. - app.vue calls setupProgressListeners() on boot. - pages/files.vue renders a per-stage label with percent inside the current step, paired with a global progress bar from the existing `progress` field. - components/ProgressLine.vue is the small bar+label widget; falls back to an indeterminate pulse during encryption (total chunks unknown). External-signer flow (start_upload / confirm_upload[_merkle]) is left dark for now — it needs an upstream ant-core PR threading the sender through `file_prepare_upload_with_visibility` and `finalize_upload[_merkle]`. Verified locally on a 25-node Anvil devnet: 3-, 9-, 10-, and 28-chunk uploads all show smooth quote/store transitions; download bars climb during chunk fetch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5 tasks
Nic-dorman
added a commit
that referenced
this pull request
Apr 30, 2026
…external-signer Builds on top of #65 (direct-path progress wiring) to also drive a live progress bar through the WalletConnect / external-signer flow, plus a small grab-bag of fixes that surfaced during end-to-end Sepolia testing. The three external-signer Tauri commands now spawn the same forwarder PR #65 introduced and pass it into ant-core's new `_with_progress` upstream methods: start_upload → file_prepare_upload_with_progress confirm_upload → finalize_upload_with_progress confirm_upload_merkle → finalize_upload_merkle_with_progress ant-core dep is pinned to the upstream PR's branch (`feat/external-signer-progress-events`) until that lands and a tag ships. Swap the dep back to a tagged `ant-core-vX.Y.Z` once #68 is merged + tagged. External-signer's transfer_id is the opaque `quote-...` string returned by `start_upload`, not the row id. The progress listener gains a `_transferIdToRowId` map, populated as soon as `getUploadQuote` knows the id (BEFORE invoking start_upload, so quote-phase events route from the very first event). The wallet/download paths still use `String(id)` and fall through to the numeric parse — both flows share one listener. - `showsProgressBar` widened to render during `quoting` / `paying` too, not just `uploading`. The bar otherwise vanished during the wallet popup and the bulk of the work on the external-signer path. - `stageDetail()` switched to per-stage percent ("Storing · 67%") instead of fractions ("Storing · 6/9"). Bar = global progress, label = local. - `StatusBadge` gets `max-w-xs truncate` + a `title` tooltip so long failure messages don't blow the table width. - **V2-236** chain auto-switch — `ensureActiveChain()` calls `switchChain` before `writeContract` so the wallet is always on the target chain. Without it, a wallet on Arbitrum One signing for an Arbitrum Sepolia tx blew up with a chain-mismatch error and no clear recovery in the UI. - **V2-231** EIP-1559 fee invariant — override BOTH `maxFeePerGas` and `maxPriorityFeePerGas` explicitly (1 gwei / 0.1 gwei). viem's partial auto-estimation produced an invalid pair on Arbitrum's tiny base fees, surfacing as either a base-fee race or a "User rejected" wrapper around the underlying invariant violation. Three sites (payForQuotes, payForMerkleTree, approve in ensureAllowance). End-to-end on a Sepolia-EVM devnet (`start-devnet-sepolia` from ant-client + MetaMask Mobile via WalletConnect, funded Sepolia wallet): - 9-chunk file, full external-signer flow: quote → MM popup → store - Bar climbs 0→50% during quoting (per-chunk ChunkQuoted), pauses at 50% during the MM popup, climbs 50→100% during storage. - Tx hash confirmed on chain, `External-signer upload finalized` logged - 21/21 vitest pass, cargo check clean against the branch ant-core dep Path-dep to upstream branch will fail any CI that pulls a registry-only ant-core. Hold until #68 lands, then bump the dep to the new tag and unmark draft. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
7 tasks
Nic-dorman
added a commit
that referenced
this pull request
Apr 30, 2026
) * feat(uploads): live progress + chain auto-switch + EIP-1559 fees for external-signer Builds on top of #65 (direct-path progress wiring) to also drive a live progress bar through the WalletConnect / external-signer flow, plus a small grab-bag of fixes that surfaced during end-to-end Sepolia testing. The three external-signer Tauri commands now spawn the same forwarder PR #65 introduced and pass it into ant-core's new `_with_progress` upstream methods: start_upload → file_prepare_upload_with_progress confirm_upload → finalize_upload_with_progress confirm_upload_merkle → finalize_upload_merkle_with_progress ant-core dep is pinned to the upstream PR's branch (`feat/external-signer-progress-events`) until that lands and a tag ships. Swap the dep back to a tagged `ant-core-vX.Y.Z` once #68 is merged + tagged. External-signer's transfer_id is the opaque `quote-...` string returned by `start_upload`, not the row id. The progress listener gains a `_transferIdToRowId` map, populated as soon as `getUploadQuote` knows the id (BEFORE invoking start_upload, so quote-phase events route from the very first event). The wallet/download paths still use `String(id)` and fall through to the numeric parse — both flows share one listener. - `showsProgressBar` widened to render during `quoting` / `paying` too, not just `uploading`. The bar otherwise vanished during the wallet popup and the bulk of the work on the external-signer path. - `stageDetail()` switched to per-stage percent ("Storing · 67%") instead of fractions ("Storing · 6/9"). Bar = global progress, label = local. - `StatusBadge` gets `max-w-xs truncate` + a `title` tooltip so long failure messages don't blow the table width. - **V2-236** chain auto-switch — `ensureActiveChain()` calls `switchChain` before `writeContract` so the wallet is always on the target chain. Without it, a wallet on Arbitrum One signing for an Arbitrum Sepolia tx blew up with a chain-mismatch error and no clear recovery in the UI. - **V2-231** EIP-1559 fee invariant — override BOTH `maxFeePerGas` and `maxPriorityFeePerGas` explicitly (1 gwei / 0.1 gwei). viem's partial auto-estimation produced an invalid pair on Arbitrum's tiny base fees, surfacing as either a base-fee race or a "User rejected" wrapper around the underlying invariant violation. Three sites (payForQuotes, payForMerkleTree, approve in ensureAllowance). End-to-end on a Sepolia-EVM devnet (`start-devnet-sepolia` from ant-client + MetaMask Mobile via WalletConnect, funded Sepolia wallet): - 9-chunk file, full external-signer flow: quote → MM popup → store - Bar climbs 0→50% during quoting (per-chunk ChunkQuoted), pauses at 50% during the MM popup, climbs 50→100% during storage. - Tx hash confirmed on chain, `External-signer upload finalized` logged - 21/21 vitest pass, cargo check clean against the branch ant-core dep Path-dep to upstream branch will fail any CI that pulls a registry-only ant-core. Hold until #68 lands, then bump the dep to the new tag and unmark draft. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(uploads/downloads): segmented bar-dot-bar progress + clearer column layout Layout changes (pages/files.vue): - Status column moved one to the left in both upload and download tables. - File name cell becomes the wider one: filename on top, progress bar below (only when active). Bar gets a min-w-[8rem] so flex-1 segments don't collapse to invisible widths in narrow rows. - Status cell stacks the badge on top with the stage detail line below it, so badge + sub-text track together visually. Progress bar (components/ProgressLine.vue + new BarSegment.vue + PayDot.vue): - Upload bar is now segmented [quote] · [pay-dot] · [store], conveying the three-phase shape of the external-signer flow at a glance. Wallet-flow and downloads use the same component but the dot stays solid (wallet) or the bar collapses to a single fill (download). - Bar fill is now stage-driven, not status-driven. Wallet-flow JS pre-flips status='uploading' before any quoting starts; the old logic took that literally and rendered the left segment full + dot solid before any work was done. New logic prefers the live `stage` from ant-core's events and uses status only as a "we're past quoting" fallback (paying / complete). - PayDot's empty state uses bg-autonomi-surface (same as the bar tracks) instead of a transparent bordered ring, for tighter visual cohesion. Status label (pages/files.vue + components/StatusBadge.vue): - statusLabel() now consults `stage` when the outer status disagrees with the inner phase. Wallet-flow uploads under stage='quoting' now show "Quoting" instead of "Uploading"; downloads under stage='resolving' show "Resolving datamap" instead of "Downloading". Badge + sub-text stay in sync. - StatusBadge: non-error labels render at natural width with whitespace-nowrap (no truncate, no max-w), so the column auto-sizes tight to its widest live label. Error labels keep `max-w-xs truncate` so a viem stacktrace can't blow the column out. - New badge entries for "Encrypting…" and "Resolving datamap". Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(uploads): persist gas_cost in upload_history.json so it survives reloads Previously the runtime FileEntry tracked gas_cost (rendered as a small "+ X gas" line under cost in the table) but the persisted UploadHistoryEntry shape didn't carry it. On app reload / Files-page refresh, every completed upload reloaded with cost intact but gas_cost dropped — the second line silently vanished. Adds optional gas_cost?: string | null on both the TS UploadHistoryEntry and the Rust struct (with #[serde(default)] for backwards compat with existing history files). loadHistory() and persistHistory() round-trip the field. Legacy entries written before this change remain valid and just render without the gas line, same as before. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(uploads/downloads): per-row Remove + Retry actions, move Clear History V2-233: each settled (failed / complete / downloaded) row now reveals a hover affordance on the right — `✕` for remove. Wires to the existing `removeEntry()` store action. V2-234: failed upload rows also show a `↻ Retry` button. Reuses `entry.path` so the user doesn't re-pick the file from disk; the new `retryUpload()` store action clears stale per-attempt fields (error, progress, stage*, address, datamap, costs, gas), flips status to `queued_for_upload`, and lets the page scheduler re-run `startRealUpload` (fresh quote + fresh wagmi sign). V2-232: bulk "Clear history" moves out of the Files page header and into Settings as a confirmation-gated action. The per-row × covers the common case of trimming one entry; bulk wipe is one click further away to prevent accidental loss. The new Settings card shows the count of settled entries that would be removed and is disabled when there's nothing to clear. Companion rename surfaced earlier in the session: external-signer's `paying` status now renders as "Awaiting approval" instead of "Paying" — more accurate while wagmi is awaiting the wallet signature, and unambiguous since the wallet flow never enters this state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * style(rust): satisfy cargo fmt on autonomi_ops.rs Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or 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
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
UploadEvent/DownloadEventchannel API (already shipped inant-core-v0.2.0) into the GUI for the direct-key wallet upload path and both download flows.(quoting * 0.5) + (storing * 0.5)so the bar moves continuously across both phases.ProgressLine.vuecomponent handles the determinate / indeterminate cases (encryption has no total until it finishes — falls back to a pulsing bar).What changed
Rust (
src-tauri/src/autonomi_ops.rs)spawn_upload_progress_forwarder/spawn_download_progress_forwarderdrain a per-call mpsc channel and re-emit each event as a Tauriupload-progress/download-progressevent keyed bytransfer_id.wallet_uploadnow callsfile_upload_with_progress(_, PaymentMode::Auto, Some(tx)).download_file/download_public/download_with_datamaptake atransfer_idarg and callfile_download_with_progress.Frontend
stores/files.ts:TransferStagetype,stage/stageDone/stageTotalonFileEntry,setupProgressListeners()action routing Tauri events back to the right row by parsingtransfer_idas the entry id.wallet_uploadinvocation now passesString(id)asuploadIdso events route correctly.app.vuecallssetupProgressListeners()on boot.pages/files.vue:stageDetail()returns the per-stage percent label;showsProgressBar()gates rendering to active uploads/downloads.What's NOT in this PR
Client::file_prepare_upload_with_visibilityandClient::finalize_upload[_merkle]don't accept a progress sender today, so threading them needs a small upstream ant-core PR. Tracked separately.UploadEvent/DownloadEventAPI is already in the pinnedant-core-v0.2.0.Test plan
cargo check,cargo clippy --all-targets -- -D warnings,cargo fmt --checkclean.npx vitest run— 21/21 pass.cargo run --release --example start-local-devnet --features devnetfromant-client) for 3-, 9-, 10-, and 28-chunk uploads. Quote phase percent climbs 0→100% in the label while bar climbs 0→50%; storage phase mirrors it 50→100%. Download by datamap shows resolving → downloading transitions.Notes for reviewers
%LOCALAPPDATA%\saorsa\bootstrap\bootstrap_cache.jsonbefore launching the devnet to keep traffic loopback-only.transfer_idrather than a random UUID, so the listener can update the right row without an extra mapping table.🤖 Generated with Claude Code