aboutsummaryrefslogtreecommitdiffstats
path: root/src/app.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-09-12 15:47:56 -0700
committerAdam Soltys <[email protected]>2026-09-12 15:47:56 -0700
commit85d7858a0ef4b4f3ee60eb2bd5621e4899195935 (patch)
treeffd283a14df52948461a1125c41bff976a4f47cc /src/app.js
parent9356684a78cddd106d82296a50a637406908cffd (diff)
downloadcoinosv3-85d7858a0ef4b4f3ee60eb2bd5621e4899195935.tar.gz
coinosv3-85d7858a0ef4b4f3ee60eb2bd5621e4899195935.zip
The wake had to be a job, not a foreground service — Android says so.
Tested on a real phone, and the first attempt didn't work at all: Background started FGS: Disallowed [... code:DENIED ...] startForegroundService() not allowed due to mAllowStartForeground false Android 12 forbids starting a foreground service from a broadcast receiver. An expedited job is the sanctioned replacement — it runs promptly from the background and needs no notification of its own to apologise for existing — so WakeService is now WakeJob, and the notification is decided by what arrived rather than by whether we could wake: a payment or a message is news either way, a wallet-connect request the wallet answers by itself is not. Two more things the phone found. A job with a connectivity constraint needs ACCESS_NETWORK_STATE, or schedule() throws and nothing runs at all. And a wake with nothing to answer used to hold a WebView open for the full thirty seconds — a locked wallet, no wallet on the device, or no NWC connections all end the job immediately now. Also a bug worth naming: ctx carried `wakeDone` by shorthand, which captured the binding while it was still undefined (ctx is built before it's defined). The page could never say it had finished, so every wake ran its full timeout. It reads through a closure now, like its neighbour. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_019mrt228Upn9nNCwGixzSLx
Diffstat (limited to 'src/app.js')
-rw-r--r--src/app.js34
1 files changed, 33 insertions, 1 deletions
diff --git a/src/app.js b/src/app.js
index 438e511..5872a74 100644
--- a/src/app.js
+++ b/src/app.js
@@ -5045,6 +5045,12 @@ const ctx = {
parseAmount: (v, u) => parseAmount(v, u, rateNow()),
worthLine,
openImage: (src) => { ui.lightbox = src; render(); },
+ // What woke this run, if anything did, and how to say we're finished.
+ // Both read through a closure: ctx is built before these are defined, and
+ // a bare `wakeDone` shorthand captured the binding while it was still
+ // undefined — the job then ran its full half minute every time.
+ wakePayload: () => WAKE_PAYLOAD,
+ wakeDone: () => wakeDone(),
// "The next balance isn't a change, it's the first real value" — state
// arriving late (the ark store opening past boot's patience) must not read
// as money landing: without this the balance counts up from 0 in green,
@@ -5124,6 +5130,23 @@ const ctx = {
// BEFORE features construct: a nostr npub/nprofile rewrites itself onto the
// existing /npub… profile deep link (which the messages feature reads at
// construction); payment URIs stash for dispatch once a wallet is open.
+// Woken by the Android wrapper (see android/, WakeJob): the wallet is being
+// loaded off-screen to deal with something. The payload is read once, here,
+// because whether anything CAN be done depends on whether a wallet opens at
+// all — and if none does, the job should end straight away rather than hold
+// a WebView open for half a minute over a locked wallet.
+const WAKE_PAYLOAD = (() => {
+ try {
+ const w = new URLSearchParams(location.search).get('wake');
+ if (w == null) return null;
+ history.replaceState(null, '', location.pathname || '/');
+ return w;
+ } catch { return null; }
+})();
+const wakeDone = () => {
+ try { if (WAKE_PAYLOAD != null && window.CoinosHost && window.CoinosHost.done) window.CoinosHost.done(); } catch {}
+};
+
const INTENT_URI = (() => {
try {
const u = new URLSearchParams(location.search).get('u');
@@ -5311,5 +5334,14 @@ loadLocale(getLang()).finally(async () => {
const storeReady = featureHook('arkStoreReady');
if (storeReady) await storeReady;
_bootDeciding = false; // from here on the screen is a decision, not a guess
- if (!restoreAccountsState()) render();
+ const opened = restoreAccountsState();
+ if (!opened) render();
+ if (WAKE_PAYLOAD != null) {
+ // Nothing opened — a locked vault, or no wallet on this device. There is
+ // nothing to answer with, so say so now.
+ if (!opened) wakeDone();
+ // And a backstop: a wallet that opened but has no feature interested in
+ // this payload must still end the job.
+ else setTimeout(wakeDone, 40_000);
+ }
});