Skip to content

feat: hide bootstrap status messages behind triple-tap easter egg#499

Closed
pietro909 wants to merge 2 commits intomasterfrom
feat/bootstrap-status-easter-egg
Closed

feat: hide bootstrap status messages behind triple-tap easter egg#499
pietro909 wants to merge 2 commits intomasterfrom
feat/bootstrap-status-easter-egg

Conversation

@pietro909
Copy link
Copy Markdown
Contributor

@pietro909 pietro909 commented Mar 31, 2026

REPLACED BY #500

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 31, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: bce61c99-bd0e-4b85-accb-13b4b2c934a0

📥 Commits

Reviewing files that changed from the base of the PR and between 954c83e and c7e86e0.

📒 Files selected for processing (2)
  • .gitignore
  • src/components/LoadingLogo.tsx

Walkthrough

Added a triple-tap gesture to the loading logo that reveals status information via a statusRevealed state. Taps within 600ms increment a counter; on the 3rd tap, visibility toggles. Also added file patterns to gitignore for .har and .agents.md files.

Changes

Cohort / File(s) Summary
Logo Interactivity
src/components/LoadingLogo.tsx
Introduced tap-to-reveal mechanism with triple-tap detection within 600ms window. Added statusRevealed state, tap-tracking refs (tapCountRef, tapTimerRef), and memoized handleLogoTap callback. Updated conditional rendering to show text and footer only when statusRevealed is true. Changed outer wrapper from motion.div to clickable div with cleanup on unmount.
Gitignore Patterns
​.gitignore
Added two ignore patterns: *.har and *.agents.md.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • sahilc0
  • bordalix
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: hiding bootstrap status messages behind a triple-tap easter egg on the loading logo.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/bootstrap-status-easter-egg

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying wallet-bitcoin with  Cloudflare Pages  Cloudflare Pages

Latest commit: 954c83e
Status: ✅  Deploy successful!
Preview URL: https://1b371cf2.wallet-bitcoin.pages.dev
Branch Preview URL: https://feat-bootstrap-status-easter.wallet-bitcoin.pages.dev

View logs

@pietro909 pietro909 requested review from bordalix and sahilc0 March 31, 2026 06:16
@arkanaai
Copy link
Copy Markdown

arkanaai bot commented Mar 31, 2026

🔍 Arkana Review — wallet#499

Code change (LoadingLogo.tsx): Clean implementation. Triple-tap with 600ms debounce, useCallback for the handler, cleanup on unmount — all good.

Issues

1. Extraneous files committed
Three files should not be in this PR:

  • BOOT_ERROR.agents.md — agent planning document (internal tooling artifact)
  • E2E_FAILING.md — debugging notes about test failures on another branch
  • arkade.money-stuck.har — HAR network trace from a production session (contains server IPs, API responses, user-agent strings)

The HAR file in particular should be removed — it's a 1100-line network capture from arkade.money that has no place in the repo.

2. Bug: exit animation background gated on statusRevealed

// Before:
{showBackground ? ( <div style={{ position: 'fixed'... }} /> ) : null}

// After:
{showBackground && statusRevealed ? ( ... ) : null}

showBackground controls the full-screen backdrop used during the exit fly-away animation (exitMode !== 'none'). Gating it on statusRevealed means the exit animation background won't render unless the user happened to triple-tap first. This looks unintentional — the exit backdrop is unrelated to status message visibility.

Fix: Remove && statusRevealed from the background conditional. Only the text rendering should be gated:

{showBackground ? ( ... ) : null}  // restore original

Summary

  • Core easter egg logic: ✅
  • Remove 3 extraneous files, fix the exit background regression, and this is good to go.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages bot commented Mar 31, 2026

Deploying wallet-mutinynet with  Cloudflare Pages  Cloudflare Pages

Latest commit: c7e86e0
Status:⚡️  Build in progress...

View logs

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@arkade.money-stuck.har`:
- Around line 1-1105: The commit includes a HAR file (arkade.money-stuck.har)
containing sensitive network telemetry that must be removed; delete the file
from the repository, commit the deletion, add a rule to prevent committing *.har
(or specifically arkade.money-stuck.har) to your ignore rules, and permanently
purge the file from git history using a history-rewrite tool (git filter-repo or
BFG) so it is not retained in prior commits; after rewriting history, force-push
the cleaned branch and coordinate any required token/secret rotations or
stakeholder notifications if the HAR contains sensitive headers.

In `@BOOT_ERROR.agents.md`:
- Around line 1-61: The BOT note (BOOT_ERROR.agents.md) is stale — it documents
the BootError/loadError flow rather than the actual logo triple-tap feature
implemented in this PR; either delete this agent note or update it to describe
the current triple-tap behavior (how LoadingLogo handles triple-tap, which
handlers in App.tsx or the WalletContext/reloadWallet flow it interacts with,
and verification steps). Remove references to BootError and loadError if they’re
not used, and ensure the doc references the concrete symbols in the branch
(LoadingLogo, the triple-tap handler, reloadWallet, and any App.tsx wiring) so
future readers find the correct implementation.

In `@E2E_FAILING.md`:
- Around line 1-80: The E2E_FAILING.md section is unrelated to this PR (it
documents feat/loading-status-messages, shouldHoldOnLoading, BootError,
loadError and E2E fixes) and should be removed or rewritten to describe the
actual change here (the LoadingLogo triple-tap / bootstrap-status easter egg);
drop the stale troubleshooting block or replace it with a short note explaining
how the triple-tap LoadingLogo/ bootstrap-status change affects E2E tests (if
any) and reference any real test changes (e.g., restore.test.ts exec ->
execAsync) so reviewers can find related symbols (LoadingLogo, triple-tap,
bootstrap-status, shouldHoldOnLoading, BootError, loadError).

In `@src/components/LoadingLogo.tsx`:
- Line 27: The component-level state statusRevealed in LoadingLogo is currently
defaulting to false and hides status text app-wide; change LoadingLogo to accept
a boolean prop hideStatusUntilRevealed (default false) and remove the internal
defaulted useState(false) behavior so non-bootstrap usages show status normally;
in App.tsx pass hideStatusUntilRevealed={true} only for the bootstrap
LoadingLogo instance (keep other callers unchanged) and ensure any local state
for revealing the easter-egg is maintained inside the bootstrap wrapper that
renders LoadingLogo rather than inside LoadingLogo itself.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d9f674c3-c245-4828-a2c9-846a2d7a5aed

📥 Commits

Reviewing files that changed from the base of the PR and between 9300408 and 954c83e.

📒 Files selected for processing (4)
  • BOOT_ERROR.agents.md
  • E2E_FAILING.md
  • arkade.money-stuck.har
  • src/components/LoadingLogo.tsx

Comment on lines +1 to +1105
{
"log": {
"version": "1.2",
"creator": {
"name": "WebInspector",
"version": "537.36"
},
"pages": [
{
"startedDateTime": "2026-03-30T13:08:56.960Z",
"id": "page_14",
"title": "https://arkade.money/",
"pageTimings": {
"onContentLoad": 299.1979999933392,
"onLoad": 412.7640000078827
}
}
],
"entries": [
{
"_connectionId": "1113342",
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "a",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 84
},
{
"functionName": "g",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 1830
},
{
"functionName": "n",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 2388
},
{
"functionName": "",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 2749
},
{
"functionName": "t",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 2819
},
{
"functionName": "",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 2999
},
{
"functionName": "",
"scriptId": "542",
"url": "https://plausible.io/js/script.js",
"lineNumber": 0,
"columnNumber": 3002
}
]
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "443",
"pageref": "page_14",
"request": {
"method": "POST",
"url": "https://plausible.io/api/event",
"httpVersion": "http/2.0",
"headers": [
{
"name": ":authority",
"value": "plausible.io"
},
{
"name": ":method",
"value": "POST"
},
{
"name": ":path",
"value": "/api/event"
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "accept",
"value": "*/*"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, br, zstd"
},
{
"name": "accept-language",
"value": "en-US,en;q=0.9"
},
{
"name": "content-length",
"value": "98"
},
{
"name": "content-type",
"value": "text/plain"
},
{
"name": "origin",
"value": "https://arkade.money"
},
{
"name": "priority",
"value": "u=1, i"
},
{
"name": "referer",
"value": "https://arkade.money/"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?1"
},
{
"name": "sec-ch-ua-platform",
"value": "\"iOS\""
},
{
"name": "sec-fetch-dest",
"value": "empty"
},
{
"name": "sec-fetch-mode",
"value": "cors"
},
{
"name": "sec-fetch-site",
"value": "cross-site"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"
}
],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 98,
"postData": {
"mimeType": "text/plain",
"text": "{\"n\":\"pageview\",\"v\":33,\"u\":\"https://arkade.money/\",\"d\":\"arkade.money\",\"r\":\"https://arkade.money/\"}"
}
},
"response": {
"status": 202,
"statusText": "",
"httpVersion": "http/2.0",
"headers": [
{
"name": "accept-ch",
"value": "Sec-CH-UA-Platform, Sec-CH-UA"
},
{
"name": "access-control-allow-credentials",
"value": "true"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "application",
"value": "127.0.0.1"
},
{
"name": "cache-control",
"value": "max-age=0, private, must-revalidate"
},
{
"name": "cdn-cachedat",
"value": "03/30/2026 13:08:57"
},
{
"name": "cdn-edgestorageid",
"value": "1332"
},
{
"name": "cdn-proxyver",
"value": "1.49"
},
{
"name": "cdn-pullzone",
"value": "682664"
},
{
"name": "cdn-requestcountrycode",
"value": "IT"
},
{
"name": "cdn-requestid",
"value": "5164b124fca7255ce0c6622360362a73"
},
{
"name": "cdn-requestpullcode",
"value": "202"
},
{
"name": "cdn-requestpullsuccess",
"value": "True"
},
{
"name": "cdn-requesttime",
"value": "0"
},
{
"name": "content-length",
"value": "2"
},
{
"name": "content-type",
"value": "text/plain; charset=utf-8"
},
{
"name": "date",
"value": "Mon, 30 Mar 2026 13:08:57 GMT"
},
{
"name": "permissions-policy",
"value": "interest-cohort=()"
},
{
"name": "server",
"value": "BunnyCDN-DE1-1332"
},
{
"name": "via",
"value": "1.1 Caddy"
},
{
"name": "x-request-id",
"value": "GKGgcW5Kge2WC4rtNiSE"
}
],
"cookies": [],
"content": {
"size": 2,
"mimeType": "text/plain",
"text": "ok"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 502,
"_error": null,
"_fetchedViaServiceWorker": false
},
"serverIPAddress": "185.111.111.158",
"startedDateTime": "2026-03-30T13:08:57.166Z",
"time": 82.97499996842816,
"timings": {
"blocked": 5.28699997517094,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.23499999999999988,
"wait": 74.31899999457598,
"receive": 3.1339999986812472,
"_blocked_queueing": 3.21499997517094,
"_workerStart": -1,
"_workerReady": -1,
"_workerFetchStart": -1,
"_workerRespondWithSettled": -1
}
},
{
"_connectionId": "1113452",
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 4471
},
{
"functionName": "Wke",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2293,
"columnNumber": 33684
},
{
"functionName": "_",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2293,
"columnNumber": 35100
},
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2293,
"columnNumber": 35185
},
{
"functionName": "B0",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 24537
},
{
"functionName": "Wd",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 43059
},
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 41277
},
{
"functionName": "_",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 24,
"columnNumber": 1585
},
{
"functionName": "H",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 24,
"columnNumber": 1944
}
],
"parentId": {
"id": "19327",
"debuggerId": "3245923585579311411.-6724205977874988118"
}
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "443",
"pageref": "page_14",
"request": {
"method": "GET",
"url": "https://blockchain.info/ticker",
"httpVersion": "http/2.0",
"headers": [
{
"name": ":authority",
"value": "blockchain.info"
},
{
"name": ":method",
"value": "GET"
},
{
"name": ":path",
"value": "/ticker"
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "accept",
"value": "*/*"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, br, zstd"
},
{
"name": "accept-language",
"value": "en-US,en;q=0.9"
},
{
"name": "if-modified-since",
"value": "Mon, 30 Mar 2026 13:08:04 GMT"
},
{
"name": "origin",
"value": "https://arkade.money"
},
{
"name": "priority",
"value": "u=1, i"
},
{
"name": "referer",
"value": "https://arkade.money/"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?1"
},
{
"name": "sec-ch-ua-platform",
"value": "\"iOS\""
},
{
"name": "sec-fetch-dest",
"value": "empty"
},
{
"name": "sec-fetch-mode",
"value": "cors"
},
{
"name": "sec-fetch-site",
"value": "cross-site"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"
}
],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 304,
"statusText": "",
"httpVersion": "http/2.0",
"headers": [
{
"name": "access-control-allow-credentials",
"value": "true"
},
{
"name": "access-control-allow-headers",
"value": "origin, content-type, accept, authorization, cookie, x-wallet-guid, x-wallet-email, user-agent, x-app-version, x-client-type, x-device-id, x-signature, x-hmac-signature, x-auth-client, cf-connecting-ip, cf-ipcity, cf-ipcountry, cf-ipcontinent, cf-iplongitude, cf-iplatitude, x-real-ip, x-payload-digest-alg, x-payload-digest, blockchain-ipcountry, blockchain-ipregion, blockchain-ipcountry-google, x-campaign, origin, blockchain-auth, blockchain-origin, blockchain-csrf, accept-language, x-agent-id, x-session-id, x-sofi-jwt-aes-ciphertext, x-sofi-aes-iv, x-sofi-aes-tag, x-sofi-aes-key-ciphertext, x-datadog-trace-id, x-datadog-parent-id, x-datadog-origin, x-datadog-sampling-priority, x-datadog-sampled, x-request-id"
},
{
"name": "access-control-allow-methods",
"value": "POST, PUT, GET, OPTIONS, DELETE, PATCH"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "age",
"value": "53"
},
{
"name": "cache-control",
"value": "public; max-age=60"
},
{
"name": "cf-cache-status",
"value": "HIT"
},
{
"name": "cf-ray",
"value": "9e4758929951ed2a-MXP"
},
{
"name": "content-security-policy",
"value": "img-src 'self' data: https://blockchain.info; style-src 'self' 'unsafe-inline'; frame-src 'none'; child-src 'none'; script-src 'self'; connect-src 'self' *.blockchain.info; object-src 'none'; media-src 'none'; font-src 'self';"
},
{
"name": "date",
"value": "Mon, 30 Mar 2026 13:08:57 GMT"
},
{
"name": "last-modified",
"value": "Mon, 30 Mar 2026 13:08:04 GMT"
},
{
"name": "server",
"value": "cloudflare"
},
{
"name": "strict-transport-security",
"value": "max-age=31536000; includeSubDomains; preload"
},
{
"name": "vary",
"value": "Accept-Encoding"
},
{
"name": "x-blockchain-cp-b",
"value": "price"
},
{
"name": "x-blockchain-cp-f",
"value": "c00b 0.005 - 3ca2e53a899d2349175799a02a965868"
},
{
"name": "x-blockchain-language",
"value": "en"
},
{
"name": "x-blockchain-language-id",
"value": "0:0:1 (en:en:en)"
},
{
"name": "x-blockchain-ms",
"value": "true"
},
{
"name": "x-blockchain-server",
"value": "BlockchainFE/1.0"
},
{
"name": "x-content-type-options",
"value": "nosniff"
},
{
"name": "x-frame-options",
"value": "SAMEORIGIN"
},
{
"name": "x-original-host",
"value": "blockchain.info"
},
{
"name": "x-request-id",
"value": "3ca2e53a899d2349175799a02a965868"
},
{
"name": "x-xss-protection",
"value": "1; mode=block"
}
],
"cookies": [],
"content": {
"size": 2763,
"mimeType": "application/json",
"text": "{\"ARS\":{\"15m\":9.406516126E7,\"last\":9.406516126E7,\"buy\":9.406516126E7,\"sell\":9.406516126E7,\"symbol\":\"ARS\"},\"AUD\":{\"15m\":98880.52,\"last\":98880.52,\"buy\":98880.52,\"sell\":98880.52,\"symbol\":\"AUD\"},\"BRL\":{\"15m\":354733.03,\"last\":354733.03,\"buy\":354733.03,\"sell\":354733.03,\"symbol\":\"BRL\"},\"CAD\":{\"15m\":94501.34,\"last\":94501.34,\"buy\":94501.34,\"sell\":94501.34,\"symbol\":\"CAD\"},\"CHF\":{\"15m\":54281.95,\"last\":54281.95,\"buy\":54281.95,\"sell\":54281.95,\"symbol\":\"CHF\"},\"CLP\":{\"15m\":6.285676334E7,\"last\":6.285676334E7,\"buy\":6.285676334E7,\"sell\":6.285676334E7,\"symbol\":\"CLP\"},\"CNY\":{\"15m\":468887.89,\"last\":468887.89,\"buy\":468887.89,\"sell\":468887.89,\"symbol\":\"CNY\"},\"CZK\":{\"15m\":1451204.64,\"last\":1451204.64,\"buy\":1451204.64,\"sell\":1451204.64,\"symbol\":\"CZK\"},\"DKK\":{\"15m\":441600.93,\"last\":441600.93,\"buy\":441600.93,\"sell\":441600.93,\"symbol\":\"DKK\"},\"EUR\":{\"15m\":59096.7,\"last\":59096.7,\"buy\":59096.7,\"sell\":59096.7,\"symbol\":\"EUR\"},\"GBP\":{\"15m\":51301.56,\"last\":51301.56,\"buy\":51301.56,\"sell\":51301.56,\"symbol\":\"GBP\"},\"GHS\":{\"15m\":744724.37,\"last\":744724.37,\"buy\":744724.37,\"sell\":744724.37,\"symbol\":\"GHS\"},\"HKD\":{\"15m\":531655.36,\"last\":531655.36,\"buy\":531655.36,\"sell\":531655.36,\"symbol\":\"HKD\"},\"HRK\":{\"15m\":312998.95,\"last\":312998.95,\"buy\":312998.95,\"sell\":312998.95,\"symbol\":\"HRK\"},\"HUF\":{\"15m\":2.295614469E7,\"last\":2.295614469E7,\"buy\":2.295614469E7,\"sell\":2.295614469E7,\"symbol\":\"HUF\"},\"INR\":{\"15m\":6423041.96,\"last\":6423041.96,\"buy\":6423041.96,\"sell\":6423041.96,\"symbol\":\"INR\"},\"ISK\":{\"15m\":8473233.45,\"last\":8473233.45,\"buy\":8473233.45,\"sell\":8473233.45,\"symbol\":\"ISK\"},\"JPY\":{\"15m\":1.081447483E7,\"last\":1.081447483E7,\"buy\":1.081447483E7,\"sell\":1.081447483E7,\"symbol\":\"JPY\"},\"KRW\":{\"15m\":1.0291540474E8,\"last\":1.0291540474E8,\"buy\":1.0291540474E8,\"sell\":1.0291540474E8,\"symbol\":\"KRW\"},\"NGN\":{\"15m\":9.388310607E7,\"last\":9.388310607E7,\"buy\":9.388310607E7,\"sell\":9.388310607E7,\"symbol\":\"NGN\"},\"NZD\":{\"15m\":118565.33,\"last\":118565.33,\"buy\":118565.33,\"sell\":118565.33,\"symbol\":\"NZD\"},\"PLN\":{\"15m\":253312.51,\"last\":253312.51,\"buy\":253312.51,\"sell\":253312.51,\"symbol\":\"PLN\"},\"RON\":{\"15m\":301289.31,\"last\":301289.31,\"buy\":301289.31,\"sell\":301289.31,\"symbol\":\"RON\"},\"RUB\":{\"15m\":5520023.18,\"last\":5520023.18,\"buy\":5520023.18,\"sell\":5520023.18,\"symbol\":\"RUB\"},\"SEK\":{\"15m\":644964.05,\"last\":644964.05,\"buy\":644964.05,\"sell\":644964.05,\"symbol\":\"SEK\"},\"SGD\":{\"15m\":87524.0,\"last\":87524.0,\"buy\":87524.0,\"sell\":87524.0,\"symbol\":\"SGD\"},\"THB\":{\"15m\":2225012.17,\"last\":2225012.17,\"buy\":2225012.17,\"sell\":2225012.17,\"symbol\":\"THB\"},\"TRY\":{\"15m\":3017588.71,\"last\":3017588.71,\"buy\":3017588.71,\"sell\":3017588.71,\"symbol\":\"TRY\"},\"TWD\":{\"15m\":2170625.24,\"last\":2170625.24,\"buy\":2170625.24,\"sell\":2170625.24,\"symbol\":\"TWD\"},\"USD\":{\"15m\":67856.42,\"last\":67856.42,\"buy\":67856.42,\"sell\":67856.42,\"symbol\":\"USD\"}}"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": 0,
"_transferSize": 78,
"_error": null,
"_fetchedViaServiceWorker": false
},
"serverIPAddress": "104.16.118.55",
"startedDateTime": "2026-03-30T13:08:57.354Z",
"time": 80.0180000369437,
"timings": {
"blocked": 20.704000025868417,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.2759999999999998,
"wait": 40.15600000713393,
"receive": 18.882000003941357,
"_blocked_queueing": 8.351000025868416,
"_workerStart": -1,
"_workerReady": -1,
"_workerFetchStart": -1,
"_workerRespondWithSettled": -1
}
},
{
"_connectionId": "1113454",
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 4471
},
{
"functionName": "getInfo",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 50,
"columnNumber": 58887
},
{
"functionName": "YH",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 26498
},
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 29048
},
{
"functionName": "B0",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 24537
},
{
"functionName": "Wd",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 43059
},
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 39,
"columnNumber": 41277
},
{
"functionName": "_",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 24,
"columnNumber": 1585
},
{
"functionName": "H",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 24,
"columnNumber": 1944
}
],
"parentId": {
"id": "19330",
"debuggerId": "3245923585579311411.-6724205977874988118"
}
}
},
"_priority": "High",
"_resourceType": "fetch",
"cache": {},
"connection": "443",
"pageref": "page_14",
"request": {
"method": "GET",
"url": "https://arkade.computer/v1/info",
"httpVersion": "http/2.0",
"headers": [
{
"name": ":authority",
"value": "arkade.computer"
},
{
"name": ":method",
"value": "GET"
},
{
"name": ":path",
"value": "/v1/info"
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "accept",
"value": "*/*"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, br, zstd"
},
{
"name": "accept-language",
"value": "en-US,en;q=0.9"
},
{
"name": "origin",
"value": "https://arkade.money"
},
{
"name": "priority",
"value": "u=1, i"
},
{
"name": "referer",
"value": "https://arkade.money/"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?1"
},
{
"name": "sec-ch-ua-platform",
"value": "\"iOS\""
},
{
"name": "sec-fetch-dest",
"value": "empty"
},
{
"name": "sec-fetch-mode",
"value": "cors"
},
{
"name": "sec-fetch-site",
"value": "cross-site"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"
}
],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 0
},
"response": {
"status": 200,
"statusText": "",
"httpVersion": "http/2.0",
"headers": [
{
"name": "access-control-allow-headers",
"value": "*"
},
{
"name": "access-control-allow-methods",
"value": "POST, GET, OPTIONS"
},
{
"name": "access-control-allow-origin",
"value": "*"
},
{
"name": "alt-svc",
"value": "h3=\":443\"; ma=86400"
},
{
"name": "cf-cache-status",
"value": "DYNAMIC"
},
{
"name": "cf-ray",
"value": "9e4758930f1bee58-MXP"
},
{
"name": "content-encoding",
"value": "zstd"
},
{
"name": "content-type",
"value": "application/json"
},
{
"name": "date",
"value": "Mon, 30 Mar 2026 13:08:57 GMT"
},
{
"name": "grpc-metadata-content-type",
"value": "application/grpc"
},
{
"name": "grpc-metadata-trailer",
"value": "Grpc-Status"
},
{
"name": "grpc-metadata-trailer",
"value": "Grpc-Message"
},
{
"name": "grpc-metadata-trailer",
"value": "Grpc-Status-Details-Bin"
},
{
"name": "nel",
"value": "{\"report_to\":\"cf-nel\",\"success_fraction\":0.0,\"max_age\":604800}"
},
{
"name": "report-to",
"value": "{\"group\":\"cf-nel\",\"max_age\":604800,\"endpoints\":[{\"url\":\"https://a.nel.cloudflare.com/report/v4?s=PRms5BiDa1jK4JglqycqUWiMihAepqrq8%2FwDXTyxlrZEyzvR1EBFjoJJwtg1BQAoYTguiONhITaGo8rDk%2FfoYtb56NFDx8HL0gc%2FB28czW0O2DrbCsxoMAKVff3JxfKdBo0%3D\"}]}"
},
{
"name": "server",
"value": "cloudflare"
}
],
"cookies": [],
"content": {
"size": 890,
"mimeType": "application/json",
"text": "{\"version\":\"v0.9.1\", \"signerPubkey\":\"022b74c2011af089c849383ee527c72325de52df6a788428b68d49e9174053aaba\", \"forfeitPubkey\":\"03b43a8363118c084a04d4f6a50ebfa58e81957f8cceceb2aee0ab64c9fd2d9977\", \"forfeitAddress\":\"bc1qzzdzp5c443vsetzatf2ra6hku322y7e5aq50rs\", \"checkpointTapscript\":\"039e0440b27520b43a8363118c084a04d4f6a50ebfa58e81957f8cceceb2aee0ab64c9fd2d9977ac\", \"network\":\"bitcoin\", \"sessionDuration\":\"60\", \"unilateralExitDelay\":\"605184\", \"boardingExitDelay\":\"7776256\", \"utxoMinAmount\":\"330\", \"utxoMaxAmount\":\"-1\", \"vtxoMinAmount\":\"1\", \"vtxoMaxAmount\":\"-1\", \"dust\":\"330\", \"fees\":{\"intentFee\":{\"offchainInput\":\"\", \"offchainOutput\":\"\", \"onchainInput\":\"\", \"onchainOutput\":\"200.0\"}, \"txFeeRate\":\"0\"}, \"scheduledSession\":null, \"deprecatedSigners\":[], \"serviceStatus\":{}, \"digest\":\"7b9820b99c0151c71fd26eb149c91c95705c7e0764dfd3455fe769065ee2c4ca\", \"maxTxWeight\":\"40000\", \"maxOpReturnOutputs\":\"2\"}"
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 811,
"_error": null,
"_fetchedViaServiceWorker": false
},
"serverIPAddress": "172.67.183.36",
"startedDateTime": "2026-03-30T13:08:57.407Z",
"time": 121.72100000316277,
"timings": {
"blocked": 27.48900003265962,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.6739999999999999,
"wait": 90.64099999435246,
"receive": 2.9169999761506915,
"_blocked_queueing": 24.16800003265962,
"_workerStart": -1,
"_workerReady": -1,
"_workerFetchStart": -1,
"_workerRespondWithSettled": -1
}
},
{
"_connectionId": "1113475",
"_initiator": {
"type": "script",
"stack": {
"callFrames": [
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 20763
},
{
"functionName": "apply",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 14218
},
{
"functionName": "",
"scriptId": "547",
"url": "https://static.cloudflareinsights.com/beacon.min.js",
"lineNumber": 0,
"columnNumber": 824
},
{
"functionName": "C",
"scriptId": "547",
"url": "https://static.cloudflareinsights.com/beacon.min.js",
"lineNumber": 0,
"columnNumber": 15153
},
{
"functionName": "U",
"scriptId": "547",
"url": "https://static.cloudflareinsights.com/beacon.min.js",
"lineNumber": 0,
"columnNumber": 16065
},
{
"functionName": "r",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 5850
}
],
"parent": {
"description": "setTimeout",
"callFrames": [
{
"functionName": "",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 20262
},
{
"functionName": "",
"scriptId": "547",
"url": "https://static.cloudflareinsights.com/beacon.min.js",
"lineNumber": 0,
"columnNumber": 16217
},
{
"functionName": "r",
"scriptId": "541",
"url": "https://arkade.money/assets/index-Cy0EdvP9.js",
"lineNumber": 2250,
"columnNumber": 5850
}
]
}
}
},
"_priority": "High",
"_resourceType": "xhr",
"cache": {},
"connection": "443",
"pageref": "page_14",
"request": {
"method": "POST",
"url": "https://cloudflareinsights.com/cdn-cgi/rum",
"httpVersion": "http/2.0",
"headers": [
{
"name": ":authority",
"value": "cloudflareinsights.com"
},
{
"name": ":method",
"value": "POST"
},
{
"name": ":path",
"value": "/cdn-cgi/rum"
},
{
"name": ":scheme",
"value": "https"
},
{
"name": "accept",
"value": "*/*"
},
{
"name": "accept-encoding",
"value": "gzip, deflate, br, zstd"
},
{
"name": "accept-language",
"value": "en-US,en;q=0.9"
},
{
"name": "content-length",
"value": "865"
},
{
"name": "content-type",
"value": "application/json"
},
{
"name": "origin",
"value": "https://arkade.money"
},
{
"name": "priority",
"value": "u=1, i"
},
{
"name": "referer",
"value": "https://arkade.money/"
},
{
"name": "sec-ch-ua",
"value": "\"Chromium\";v=\"146\", \"Not-A.Brand\";v=\"24\", \"Google Chrome\";v=\"146\""
},
{
"name": "sec-ch-ua-mobile",
"value": "?1"
},
{
"name": "sec-ch-ua-platform",
"value": "\"iOS\""
},
{
"name": "sec-fetch-dest",
"value": "empty"
},
{
"name": "sec-fetch-mode",
"value": "cors"
},
{
"name": "sec-fetch-site",
"value": "cross-site"
},
{
"name": "user-agent",
"value": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1"
}
],
"queryString": [],
"cookies": [],
"headersSize": -1,
"bodySize": 865,
"postData": {
"mimeType": "application/json",
"text": "{\"memory\":{\"totalJSHeapSize\":143454430,\"usedJSHeapSize\":83081858,\"jsHeapSizeLimit\":4294967296},\"resources\":[],\"referrer\":\"https://arkade.money/\",\"eventType\":1,\"firstPaint\":0,\"firstContentfulPaint\":0,\"startTime\":1774876136953.2,\"versions\":{\"js\":\"2026.2.0\",\"timings\":2},\"pageloadId\":\"23c93098-8d93-4934-873f-44cd115efff2\",\"location\":\"https://arkade.money/\",\"nt\":\"reload\",\"timingsV2\":{\"nextHopProtocol\":\"h2\",\"domainLookupStart\":4.5,\"domainLookupEnd\":4.5,\"connectStart\":4.5,\"connectEnd\":4.5,\"requestStart\":9.5,\"responseStart\":83.5,\"responseEnd\":87.40000003576279,\"domInteractive\":211.60000002384186,\"domComplete\":417.80000001192093,\"loadEventStart\":418.7000000476837,\"loadEventEnd\":420.2000000476837,\"finalResponseHeadersStart\":83.5,\"firstInterimResponseStart\":0,\"transferSize\":1810,\"decodedBodySize\":4001},\"dt\":\"\",\"siteToken\":\"913c75c682fb427c90e6069b57ce1a11\",\"st\":2}"
}
},
"response": {
"status": 204,
"statusText": "",
"httpVersion": "http/2.0",
"headers": [
{
"name": "access-control-allow-credentials",
"value": "true"
},
{
"name": "access-control-allow-methods",
"value": "POST,OPTIONS"
},
{
"name": "access-control-allow-origin",
"value": "https://arkade.money"
},
{
"name": "access-control-max-age",
"value": "86400"
},
{
"name": "cf-ray",
"value": "9e47589309a8ed5a-MXP"
},
{
"name": "content-type",
"value": "text/plain"
},
{
"name": "date",
"value": "Mon, 30 Mar 2026 13:08:57 GMT"
},
{
"name": "server",
"value": "cloudflare"
},
{
"name": "vary",
"value": "Origin"
}
],
"cookies": [],
"content": {
"size": 0,
"mimeType": "text/plain",
"text": ""
},
"redirectURL": "",
"headersSize": -1,
"bodySize": -1,
"_transferSize": 59,
"_error": null,
"_fetchedViaServiceWorker": false
},
"serverIPAddress": "104.16.80.73",
"startedDateTime": "2026-03-30T13:08:57.435Z",
"time": 70.06800000090152,
"timings": {
"blocked": 4.588000037692487,
"dns": -1,
"ssl": -1,
"connect": -1,
"send": 0.19899999999999984,
"wait": 55.02699999887496,
"receive": 10.25399996433407,
"_blocked_queueing": 2.4970000376924872,
"_workerStart": -1,
"_workerReady": -1,
"_workerFetchStart": -1,
"_workerRespondWithSettled": -1
}
}
]
}
} No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Remove this raw HAR artifact from the repository.

This file stores full network telemetry (headers/body/fingerprinting metadata) and is out of scope for the feature. Keeping it in git introduces avoidable privacy/compliance risk and permanent history retention of captured traffic.

🧹 Proposed fix
--- a/arkade.money-stuck.har
+++ /dev/null
@@ -1,1105 +0,0 @@
-{
-  ...
-}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@arkade.money-stuck.har` around lines 1 - 1105, The commit includes a HAR file
(arkade.money-stuck.har) containing sensitive network telemetry that must be
removed; delete the file from the repository, commit the deletion, add a rule to
prevent committing *.har (or specifically arkade.money-stuck.har) to your ignore
rules, and permanently purge the file from git history using a history-rewrite
tool (git filter-repo or BFG) so it is not retained in prior commits; after
rewriting history, force-push the cleaned branch and coordinate any required
token/secret rotations or stakeholder notifications if the HAR contains
sensitive headers.

Comment on lines +1 to +61
# Plan: Retry prompt when first wallet data load fails

## Context

After commit 0b4da778 fixed the loading-screen deadlock by setting `dataReady = true` in a `finally` block, a failed first `reloadWallet` silently falls through to an empty wallet with no error or retry path. We want to keep the user on the boot loading screen with a retry prompt instead.

## Approach

Replace the "always set dataReady on failure" strategy with an explicit error state that holds the loading gate open and shows a retry UI.

### 1. Add `loadError` state to WalletContext (`src/providers/wallet.tsx`)

- New state: `const [loadError, setLoadError] = useState<string | null>(null)`
- Add to `WalletContextProps` interface, default context value, and provider value
- Expose `dismissLoadError` function that sets `dataReady = true` (continue to empty wallet)

### 2. Modify `reloadWallet` catch/finally (`src/providers/wallet.tsx:~308-318`)

- **catch block**: On first load (`!hasLoadedOnce.current`), set `loadError` with a user-facing message, call `clearLoadingStatus()`, but do NOT set `dataReady = true` or `hasLoadedOnce = true`. This keeps `shouldHoldOnLoading` true so the user stays on the loading screen.
- **finally block**: Remove entirely — the catch now handles the first-load failure, and the try block already handles success.
- On retry: `reloadWallet` clears `loadError` at entry before trying again.

### 3. New component: `BootError` (`src/components/BootError.tsx`)

A full-screen overlay (matching LoadingLogo's visual layer) that shows:
- Centered layout using inline styles matching CenterScreen's pattern (flex column, centered, max-width 18rem)
- Themed background (`var(--ion-background-color)`) covering the full viewport like LoadingLogo's portal
- Error text: `Text` component with the error message
- "Retry" button: primary `Button` with `loading` state while retrying
- "Continue anyway" button: secondary/clear `Button` that calls `dismissLoadError`

Rendered via `createPortal` to `document.body` (same as LoadingLogo) so it sits at the same z-layer.

### 4. Wire up in App.tsx (`src/App.tsx:~385-392`)

Replace the boot animation block:
```tsx
{bootAnimActive ? (
loadError ? (
<BootError />
) : (
<LoadingLogo ... />
)
) : null}
```

Pull `loadError`, `reloadWallet`, `dismissLoadError` from `WalletContext`.

## Files to modify

| File | Change |
|---|---|
| `src/providers/wallet.tsx` | Add `loadError` state, modify `reloadWallet` catch/finally, add `dismissLoadError`, update interface + provider value |
| `src/components/BootError.tsx` | **New file** — retry prompt component |
| `src/App.tsx` | Destructure new context fields, conditionally render `BootError` vs `LoadingLogo` |

## Verification

1. `pnpm build` — no type errors
2. `pnpm lint && pnpm format:check` — clean
3. Manual test: temporarily inject `throw new Error('test')` at the top of `reloadWallet`'s try block, verify the retry prompt appears on boot, retry works, "continue anyway" proceeds to empty wallet
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

This agent note is out of sync with the feature in this PR.

The file still describes the earlier BootError / loadError plan rather than the logo triple-tap behavior being added here. If it lands as-is, future readers will chase the wrong implementation details. Please remove it from this branch or update it to the current feature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@BOOT_ERROR.agents.md` around lines 1 - 61, The BOT note
(BOOT_ERROR.agents.md) is stale — it documents the BootError/loadError flow
rather than the actual logo triple-tap feature implemented in this PR; either
delete this agent note or update it to describe the current triple-tap behavior
(how LoadingLogo handles triple-tap, which handlers in App.tsx or the
WalletContext/reloadWallet flow it interacts with, and verification steps).
Remove references to BootError and loadError if they’re not used, and ensure the
doc references the concrete symbols in the branch (LoadingLogo, the triple-tap
handler, reloadWallet, and any App.tsx wiring) so future readers find the
correct implementation.

E2E_FAILING.md Outdated
Comment on lines +1 to +80
# E2E Test Failures on `feat/loading-status-messages`

## Root Cause

The branch changed `shouldHoldOnLoading` in `App.tsx:223` from:

```js
// master:
const shouldHoldOnLoading = hasStoredWallet && !initialized && authState !== 'locked'
// branch:
const shouldHoldOnLoading = hasStoredWallet && (!initialized || !dataReady) && authState !== 'locked'
```

This keeps the loading screen visible (and `comp = null` — no page content rendered) until `reloadWallet()` completes and sets `dataReady = true`. On master, the wallet page renders immediately after `initialized` becomes true.

Two consequences for tests:

1. **Wallet readiness takes longer** — `createWallet()` and `restoreWallet()` waited for `text=Send` with a 30s timeout. On this branch, `Send` only appears after `reloadWallet` finishes (extra seconds). If `reloadWallet` fails, a `BootError` overlay ("Continue anyway" / "Retry") blocks the UI instead.

2. **`reloadWallet` failure on first load blocks the app** — On master, failures were silently ignored. On this branch, `loadError` is set and `BootError` renders as a full-screen overlay. Tests don't handle this.

## Fixes Applied

### 1. `waitForWalletPage` helper (`src/test/e2e/utils.ts`)

Handles both the normal path (`Send` visible) and the error path (`Continue anyway` visible):

```ts
export async function waitForWalletPage(page: Page, timeout = 60000): Promise<void> {
const sendBtn = page.getByText('Send', { exact: true })
const continueBtn = page.getByText('Continue anyway')
await sendBtn.or(continueBtn).first().waitFor({ state: 'visible', timeout })
if (await continueBtn.isVisible()) {
await continueBtn.click()
await sendBtn.waitFor({ state: 'visible', timeout: 30000 })
}
}
```

Used in `createWallet()`, `restoreWallet()`, and `delegate.test.ts` (post-reload wait).

### 2. `waitForPaymentReceived` timeout (`src/test/e2e/utils.ts`)

Increased from default 30s to 60s. The Boltz reverse-swap claim path can exceed 30s on slow CI runners.

## Remaining Issue: `restore.test.ts` Lightning payment not detected

### Symptom

`waitForPaymentReceived` times out even at 60s. The page snapshot shows the QrCode/Receive page is correctly displaying the Lightning invoice, but "Payment received!" never appears.

### Key finding: `exec` vs `execSync`

The test uses fire-and-forget `exec()` to pay the Lightning invoice via lnd:
```js
exec(`docker exec lnd lncli --network=regtest payinvoice ${invoice} --force`)
```

- **`execSync`** (blocking): payment works instantly, test passes
- **`exec`** (fire-and-forget): payment never completes, test times out

On master, fire-and-forget `exec` works fine. On this branch, it does not.

### Cascading failure

Each failed attempt leaves an IN_FLIGHT HTLC in lnd that never settles. After several failed runs, lnd accumulates stuck HTLCs that block all new payments on the channel. This makes subsequent runs fail even more reliably.

### Theories investigated but not confirmed

- **Navigation effect re-firing**: The `wallet.tsx:256-278` effect navigates to `Pages.Wallet` when `initialized && dataReady`. If `initialized` flickers (status ping returning false temporarily), the effect could re-fire and bounce the user off the QrCode page, unmounting the payment listener. However, the same effect exists on master.

- **QrCode component unmount**: If `shouldHoldOnLoading` becomes true mid-test (e.g., `initialized` flickers), `comp = null` destroys all page content including the QrCode component and its `listenForPayments` handler. The swap's `waitAndClaim` promise and the VTXO_UPDATE listener are lost. This is unique to the branch since master's `shouldHoldOnLoading` is less likely to re-trigger.

- **`exec` child process blocking on stdout pipe**: Unlikely given the small output size of `payinvoice`.

### Suggested next step

Change the `exec` call at `restore.test.ts:44` (and similar in `swap.test.ts:40`, `nostr.test.ts:84`) to use `execAsync` so the payment is confirmed before waiting for wallet detection. The test already imports `execAsync = promisify(exec)`.

Alternatively, investigate why `exec` (fire-and-forget) works on master but not on this branch — likely related to the `initialized` state flickering causing component unmounts via `shouldHoldOnLoading`.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

This note looks copied from a different branch.

It documents feat/loading-status-messages, shouldHoldOnLoading, BootError, and loadError, which are unrelated to the triple-tap LoadingLogo change in this PR. Keeping it here will leave stale troubleshooting docs in-tree. Please drop it from this PR or rewrite it to cover the actual e2e impact of the bootstrap-status easter egg.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@E2E_FAILING.md` around lines 1 - 80, The E2E_FAILING.md section is unrelated
to this PR (it documents feat/loading-status-messages, shouldHoldOnLoading,
BootError, loadError and E2E fixes) and should be removed or rewritten to
describe the actual change here (the LoadingLogo triple-tap / bootstrap-status
easter egg); drop the stale troubleshooting block or replace it with a short
note explaining how the triple-tap LoadingLogo/ bootstrap-status change affects
E2E tests (if any) and reference any real test changes (e.g., restore.test.ts
exec -> execAsync) so reviewers can find related symbols (LoadingLogo,
triple-tap, bootstrap-status, shouldHoldOnLoading, BootError, loadError).

export default function LoadingLogo({ text, done, exitMode = 'none', onExitComplete }: LoadingLogoProps) {
const reducedMotion = useReducedMotion()
const [visible, setVisible] = useState(true)
const [statusRevealed, setStatusRevealed] = useState(false)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Scope the hidden-status behavior to the boot screen only.

LoadingLogo is also used in src/screens/Init/Connect.tsx:1-50, src/screens/Wallet/Send/Details.tsx:1-50, and src/screens/Wallet/Notes/Redeem.tsx:1-50, so defaulting statusRevealed to false here now hides "Connecting…", "Paying…", and "Processing…" across the app until the easter egg is tapped. That looks broader than the PR goal and removes important progress feedback from non-bootstrap flows.

💡 Minimal scoping fix
 interface LoadingLogoProps {
   text?: string
   done?: boolean
   exitMode?: 'fly-to-target' | 'fly-up' | 'none'
   onExitComplete?: () => void
+  hideStatusUntilRevealed?: boolean
 }

-export default function LoadingLogo({ text, done, exitMode = 'none', onExitComplete }: LoadingLogoProps) {
+export default function LoadingLogo({
+  text,
+  done,
+  exitMode = 'none',
+  onExitComplete,
+  hideStatusUntilRevealed = false,
+}: LoadingLogoProps) {
   // ...
-        {text && statusRevealed ? (
+        {text && (!hideStatusUntilRevealed || statusRevealed) ? (
           <motion.div
             style={{ paddingTop: '0.5rem' }}
             animate={{ opacity: exiting ? 0 : 1 }}
             transition={{ duration: 0.2, ease: EASE_OUT_QUINT_TUPLE }}
           >
             <Text centered small wrap heading>
               {text}
             </Text>
           </motion.div>
         ) : null}
       </div>
-      {showBackground && statusRevealed ? (
+      {showBackground && (!hideStatusUntilRevealed || statusRevealed) ? (
         <div
           style={{
             position: 'fixed',
             bottom: '1rem',

Then pass hideStatusUntilRevealed only from src/App.tsx's bootstrap instance.

Also applies to: 183-195

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/components/LoadingLogo.tsx` at line 27, The component-level state
statusRevealed in LoadingLogo is currently defaulting to false and hides status
text app-wide; change LoadingLogo to accept a boolean prop
hideStatusUntilRevealed (default false) and remove the internal defaulted
useState(false) behavior so non-bootstrap usages show status normally; in
App.tsx pass hideStatusUntilRevealed={true} only for the bootstrap LoadingLogo
instance (keep other callers unchanged) and ensure any local state for revealing
the easter-egg is maintained inside the bootstrap wrapper that renders
LoadingLogo rather than inside LoadingLogo itself.

pietro909 and others added 2 commits March 31, 2026 08:36
Status messages and commit hash during wallet initialization are now hidden
by default. Tap the loading logo three times (within 600ms between taps) to
toggle them on/off. This keeps the boot animation clean for normal users while
preserving debug visibility for developers and testers.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
@pietro909 pietro909 force-pushed the feat/bootstrap-status-easter-egg branch from 954c83e to c7e86e0 Compare March 31, 2026 06:37
@pietro909
Copy link
Copy Markdown
Contributor Author

Messed up with files, sry

@pietro909 pietro909 closed this Mar 31, 2026
@pietro909
Copy link
Copy Markdown
Contributor Author

#500

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant