aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/features/nwc.js45
-rw-r--r--src/i18n.js4
2 files changed, 43 insertions, 6 deletions
diff --git a/src/features/nwc.js b/src/features/nwc.js
index 730299e..686b11f 100644
--- a/src/features/nwc.js
+++ b/src/features/nwc.js
@@ -571,11 +571,16 @@ export function nwcFeature(ctx) {
});
const pks1 = conns().map((c) => c.servicePk);
if (load().offer) pks1.push(load().offer.pk);
- const r = await fetch(`${NOTIFIER}/register`, {
- method: 'POST', headers: { 'content-type': 'application/json' },
- body: JSON.stringify({ subscription: sub.toJSON(), servicePubkeys: pks1 }),
- });
- if (!r.ok) throw new Error(`notifier refused: ${r.status}`);
+ // Arming can precede the first connection (creation requires it) — the
+ // notifier rejects an empty watch list, so defer that POST to the
+ // refreshRegistration() the creation triggers moments later.
+ if (pks1.length) {
+ const r = await fetch(`${NOTIFIER}/register`, {
+ method: 'POST', headers: { 'content-type': 'application/json' },
+ body: JSON.stringify({ subscription: sub.toJSON(), servicePubkeys: pks1 }),
+ });
+ if (!r.ok) throw new Error(`notifier refused: ${r.status}`);
+ }
const st = load(); st.background = true; save(st);
writeBg();
return true;
@@ -602,6 +607,27 @@ export function nwcFeature(ctx) {
} catch (e) { console.log('nwc: background answering not armed:', e.message); }
}
+ // A connection that only answers while a tab happens to be open is a broken
+ // promise — creating one REQUIRES the background path. Throws a
+ // user-readable reason when it can't be armed; the caller shows it and
+ // does not create the connection.
+ async function requireBackground() {
+ if (load().background) return;
+ if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)
+ || typeof window === 'undefined' || !('PushManager' in window)
+ || typeof Notification === 'undefined') throw new Error(t('nwcNoPush'));
+ if (Notification.permission === 'denied') throw new Error(t('nwcNotifBlocked'));
+ try {
+ await enableBackground();
+ } catch (e) {
+ // Denied at the prompt (or dismissed it) — anything else is a real
+ // failure whose message is more useful than a permissions lecture.
+ if (Notification.permission !== 'granted') throw new Error(t('nwcNotifNeeded'));
+ throw e;
+ }
+ const s = load(); delete s.backgroundOff; save(s);
+ }
+
// Keep the notifier's view of our service pubkeys current. It can only
// wake this device for connections it was told about, and a registration
// made before a connection existed (or after one was revoked) watches the
@@ -779,9 +805,16 @@ export function nwcFeature(ctx) {
h('label', { class: 'field' }, h('span', { class: 'lab' }, t('nwcDailyBudget')),
h('input', { type: 'number', min: '1', value: st.dailySat,
onInput: (e) => { st.dailySat = e.target.value; } })),
+ (typeof Notification === 'undefined' || Notification.permission !== 'granted')
+ ? h('div', { class: 'small faint' }, t('nwcNotifHint'))
+ : null,
h('div', { class: 'row gap6' },
h('button', { class: 'btn-ghost', onClick: () => { ui.nwcNew = null; render(); } }, t('cancel')),
- h('button', { class: 'btn-primary grow', onClick: () => {
+ h('button', { class: 'btn-primary grow', onClick: async () => {
+ // No notifications, no connection: it would sit dead the
+ // moment the tab closes, spinning in the other app.
+ try { await requireBackground(); }
+ catch (e) { toast(e.message); return; }
const c = createConn({
name: st.name, maxSat: parseInt(st.maxSat, 10) || 10000,
dailySat: parseInt(st.dailySat, 10) || 50000,
diff --git a/src/i18n.js b/src/i18n.js
index fde72fd..85f8308 100644
--- a/src/i18n.js
+++ b/src/i18n.js
@@ -663,6 +663,10 @@ const STR = {
nwcOff: 'Off',
nwcBackgroundOn: 'Requests will be answered in the background',
nwcBackgroundOff: 'Background answering turned off',
+ nwcNotifHint: 'You’ll be asked to allow notifications — the connection uses them to answer when Coinos is closed.',
+ nwcNotifNeeded: 'Without notifications the connection couldn’t answer when Coinos is closed, so it wasn’t added. Allow notifications and try again.',
+ nwcNotifBlocked: 'Notifications are blocked for this site. Allow them in your browser settings, then add the connection again.',
+ nwcNoPush: 'This browser can’t receive push notifications, so a connection wouldn’t work while Coinos is closed.',
nwcAppName: 'App name (e.g. Damus)',
nwcMaxPayment: 'Max per payment (sats)',
nwcDailyBudget: 'Daily budget (sats)',