@openreceive/elements 0.1.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/chunk-46RMHGRA.js +2488 -0
- package/dist/define-elements-CX8Ml6J4.d.ts +108 -0
- package/dist/index.d.ts +41 -41
- package/dist/index.js +30 -781
- package/dist/styles.css +3 -1
- package/dist/wrapper-shared.d.ts +53 -0
- package/dist/wrapper-shared.js +86 -0
- package/package.json +51 -7
|
@@ -0,0 +1,2488 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import {
|
|
3
|
+
createThemeToggleElement,
|
|
4
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES as OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES4,
|
|
5
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS as OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS2,
|
|
6
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME as OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME2,
|
|
7
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES as OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES2,
|
|
8
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_EVENTS,
|
|
9
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME as OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME2
|
|
10
|
+
} from "@openreceive/browser/headless";
|
|
11
|
+
|
|
12
|
+
// src/define-elements.ts
|
|
13
|
+
import {
|
|
14
|
+
buildMethodGridEntries as buildMethodGridEntries2,
|
|
15
|
+
copyInvoice,
|
|
16
|
+
createCheckoutActionEvent,
|
|
17
|
+
createCheckoutController,
|
|
18
|
+
createCheckoutErrorEvent,
|
|
19
|
+
createCheckoutProviderCopyEvent,
|
|
20
|
+
createCheckoutSnapshotFromInvoice as createCheckoutSnapshotFromInvoice2,
|
|
21
|
+
createCheckoutStateEvent,
|
|
22
|
+
createCheckoutStatusModel as createCheckoutStatusModel2,
|
|
23
|
+
createPaymentWizardSelection,
|
|
24
|
+
createSwapDisplayModel as createSwapDisplayModel2,
|
|
25
|
+
createThemeChangeEvent,
|
|
26
|
+
createQrPayloadSvg,
|
|
27
|
+
createQrSvg,
|
|
28
|
+
deriveStatus as deriveStatus2,
|
|
29
|
+
enterCheckoutResumePath,
|
|
30
|
+
findSwapGridGroup as findSwapGridGroup2,
|
|
31
|
+
getSwapRefundFormError,
|
|
32
|
+
OPENRECEIVE_CHECKOUT_DATA_SELECTORS,
|
|
33
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES as OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3,
|
|
34
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS,
|
|
35
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_PART_SELECTORS,
|
|
36
|
+
OPENRECEIVE_DEFAULT_PREFIX as OPENRECEIVE_DEFAULT_PREFIX2,
|
|
37
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7,
|
|
38
|
+
OPENRECEIVE_PAYMENT_WIZARD_SELECTORS as OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3,
|
|
39
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES,
|
|
40
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_PART_SELECTORS,
|
|
41
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME,
|
|
42
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME,
|
|
43
|
+
paymentMethods as paymentMethods2,
|
|
44
|
+
orClasses as orClasses7,
|
|
45
|
+
overlaySwapRefundStaging,
|
|
46
|
+
parseBooleanAttribute,
|
|
47
|
+
parseMethodPickerKey,
|
|
48
|
+
parseOptionalInteger,
|
|
49
|
+
parsePaymentMethod,
|
|
50
|
+
parseResolvedTheme,
|
|
51
|
+
parseThemePreference,
|
|
52
|
+
requestSwapRefund,
|
|
53
|
+
syncStoredThemeControls,
|
|
54
|
+
toggleStoredThemeControls,
|
|
55
|
+
updatePaymentWizardSelection,
|
|
56
|
+
updateSelectedSwapNetworks
|
|
57
|
+
} from "@openreceive/browser/headless";
|
|
58
|
+
|
|
59
|
+
// src/element-checkout-session.ts
|
|
60
|
+
import {
|
|
61
|
+
applyCheckoutElementAttributes,
|
|
62
|
+
createCheckoutElementAttributes,
|
|
63
|
+
createCheckoutSession,
|
|
64
|
+
mergeAttemptIntoCheckout,
|
|
65
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES,
|
|
66
|
+
OPENRECEIVE_DEFAULT_PREFIX,
|
|
67
|
+
prepareCheckout,
|
|
68
|
+
requestCheckout
|
|
69
|
+
} from "@openreceive/browser/headless";
|
|
70
|
+
function createElementCheckoutSession(host) {
|
|
71
|
+
let creating = false;
|
|
72
|
+
let createdKey;
|
|
73
|
+
let createError;
|
|
74
|
+
let applyingOwnAttributes = 0;
|
|
75
|
+
function currentPrefix() {
|
|
76
|
+
return host.element.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.prefix) ?? OPENRECEIVE_DEFAULT_PREFIX;
|
|
77
|
+
}
|
|
78
|
+
function currentReference() {
|
|
79
|
+
const reference = host.element.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES.reference);
|
|
80
|
+
return reference === null || reference.length === 0 ? void 0 : reference;
|
|
81
|
+
}
|
|
82
|
+
function currentCreateKey() {
|
|
83
|
+
const reference = currentReference();
|
|
84
|
+
return reference === void 0 ? void 0 : `${currentPrefix()}::${reference}`;
|
|
85
|
+
}
|
|
86
|
+
function writeOwnAttributes(write) {
|
|
87
|
+
applyingOwnAttributes += 1;
|
|
88
|
+
try {
|
|
89
|
+
write();
|
|
90
|
+
} finally {
|
|
91
|
+
applyingOwnAttributes -= 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function applyOwnAttributes(attributes) {
|
|
95
|
+
writeOwnAttributes(() => {
|
|
96
|
+
applyCheckoutElementAttributes(host.element, attributes);
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
function publishSnapshot(snapshot) {
|
|
100
|
+
host.handleControllerSnapshot(snapshot);
|
|
101
|
+
const prefix = currentPrefix();
|
|
102
|
+
applyOwnAttributes(
|
|
103
|
+
createCheckoutElementAttributes(snapshot, {
|
|
104
|
+
prefix,
|
|
105
|
+
...host.currentThemeOption()
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
host.render();
|
|
109
|
+
host.startCheckoutController();
|
|
110
|
+
}
|
|
111
|
+
const session = createCheckoutSession({
|
|
112
|
+
snapshot: () => host.latestCheckoutSnapshot(),
|
|
113
|
+
reference: currentReference,
|
|
114
|
+
requestCheckout: (reference) => {
|
|
115
|
+
const metadata = host.createMetadata();
|
|
116
|
+
return requestCheckout({
|
|
117
|
+
prefix: currentPrefix(),
|
|
118
|
+
reference,
|
|
119
|
+
...metadata === void 0 ? {} : { metadata },
|
|
120
|
+
fetch: globalThis.fetch
|
|
121
|
+
});
|
|
122
|
+
},
|
|
123
|
+
onSnapshot: publishSnapshot,
|
|
124
|
+
swapPrefix: () => host.resolvePollPrefix(currentReference()),
|
|
125
|
+
fetch: () => globalThis.fetch,
|
|
126
|
+
swapSelection: host.swapSelection,
|
|
127
|
+
// A swap attempt is NOT written back as attributes: a bolt11 attribute
|
|
128
|
+
// would take the element out of create mode. It re-keys the poll
|
|
129
|
+
// controller onto the merged snapshot and nothing else.
|
|
130
|
+
onSwapStarted: (invoice) => {
|
|
131
|
+
const reference = currentReference();
|
|
132
|
+
if (reference === void 0) return;
|
|
133
|
+
const previous = host.latestCheckoutSnapshot() ?? host.currentCheckoutSnapshot();
|
|
134
|
+
host.handleControllerSnapshot(mergeAttemptIntoCheckout(invoice, previous, reference));
|
|
135
|
+
host.startCheckoutController();
|
|
136
|
+
},
|
|
137
|
+
logger: host.logger,
|
|
138
|
+
onError: (error) => host.dispatchError(error),
|
|
139
|
+
onChange: () => host.render()
|
|
140
|
+
});
|
|
141
|
+
async function createCheckout() {
|
|
142
|
+
const reference = currentReference();
|
|
143
|
+
if (reference === void 0) return;
|
|
144
|
+
const prefix = currentPrefix();
|
|
145
|
+
const key = `${prefix}::${reference}`;
|
|
146
|
+
if (creating) return;
|
|
147
|
+
if (createdKey === key) {
|
|
148
|
+
host.startCheckoutController();
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
creating = true;
|
|
152
|
+
createdKey = key;
|
|
153
|
+
session.resetLightningRequest();
|
|
154
|
+
try {
|
|
155
|
+
createError = void 0;
|
|
156
|
+
host.syncResumePath(reference);
|
|
157
|
+
const checkout = await prepareCheckout({
|
|
158
|
+
prefix,
|
|
159
|
+
reference,
|
|
160
|
+
fetch: globalThis.fetch
|
|
161
|
+
});
|
|
162
|
+
if (currentCreateKey() !== key) return;
|
|
163
|
+
host.handleControllerSnapshot(checkout);
|
|
164
|
+
applyOwnAttributes(
|
|
165
|
+
createCheckoutElementAttributes(checkout, {
|
|
166
|
+
prefix,
|
|
167
|
+
...host.currentThemeOption()
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
host.render();
|
|
171
|
+
host.startCheckoutController();
|
|
172
|
+
} catch (error) {
|
|
173
|
+
if (currentCreateKey() !== key) return;
|
|
174
|
+
createError = error instanceof Error && error.message.length > 0 ? error.message : "Could not start checkout.";
|
|
175
|
+
host.dispatchError(error);
|
|
176
|
+
host.render();
|
|
177
|
+
} finally {
|
|
178
|
+
creating = false;
|
|
179
|
+
const current = currentCreateKey();
|
|
180
|
+
if (host.element.isConnected && current !== void 0 && current !== key && host.isCreateMode()) {
|
|
181
|
+
void createCheckout();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function retryCreateCheckout() {
|
|
186
|
+
createdKey = void 0;
|
|
187
|
+
createError = void 0;
|
|
188
|
+
host.render();
|
|
189
|
+
void createCheckout();
|
|
190
|
+
}
|
|
191
|
+
return {
|
|
192
|
+
get createError() {
|
|
193
|
+
return createError;
|
|
194
|
+
},
|
|
195
|
+
get wizardError() {
|
|
196
|
+
return session.wizardError;
|
|
197
|
+
},
|
|
198
|
+
get swapStartError() {
|
|
199
|
+
return session.swapStartError;
|
|
200
|
+
},
|
|
201
|
+
get lightningRequested() {
|
|
202
|
+
return session.lightningRequested;
|
|
203
|
+
},
|
|
204
|
+
get mintingLightning() {
|
|
205
|
+
return session.mintingLightning;
|
|
206
|
+
},
|
|
207
|
+
get startingSwapAsset() {
|
|
208
|
+
return session.startingSwapAsset;
|
|
209
|
+
},
|
|
210
|
+
get swapQuotes() {
|
|
211
|
+
return session.swapQuotes;
|
|
212
|
+
},
|
|
213
|
+
get applyingOwnAttributes() {
|
|
214
|
+
return applyingOwnAttributes > 0;
|
|
215
|
+
},
|
|
216
|
+
createCheckout,
|
|
217
|
+
retryCreateCheckout,
|
|
218
|
+
ensureLightning: () => session.ensureLightning(),
|
|
219
|
+
startSwap: (payInAsset) => session.startSwap(payInAsset),
|
|
220
|
+
applyOwnAttributes,
|
|
221
|
+
writeOwnAttributes,
|
|
222
|
+
forgetCreateKey() {
|
|
223
|
+
createdKey = void 0;
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// src/dom-helpers.ts
|
|
229
|
+
import {
|
|
230
|
+
createDetailExternalLink,
|
|
231
|
+
createTransientFeedbackController,
|
|
232
|
+
escapeHtml,
|
|
233
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES as OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES2,
|
|
234
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES,
|
|
235
|
+
OPENRECEIVE_PAYMENT_WIZARD_SELECTORS,
|
|
236
|
+
checkoutLabels,
|
|
237
|
+
orClasses
|
|
238
|
+
} from "@openreceive/browser/headless";
|
|
239
|
+
var COPY_INVOICE_ICON = `<svg class="${orClasses.copyIcon}" width="16" height="16" viewBox="0 0 16 16" fill="none" aria-hidden="true" focusable="false"><rect x="5" y="5" width="8" height="9" rx="1.5" stroke="currentColor" stroke-width="1.5"/><path d="M3.5 11V3.5A1.5 1.5 0 0 1 5 2h5.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/></svg>`;
|
|
240
|
+
var elementCopyFeedbackControllers = /* @__PURE__ */ new WeakMap();
|
|
241
|
+
function readElementFiatQuote(element) {
|
|
242
|
+
const currency = element.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES2.fiatCurrency);
|
|
243
|
+
const value = element.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES2.fiatValue);
|
|
244
|
+
if (currency === null || value === null) return void 0;
|
|
245
|
+
return {
|
|
246
|
+
fiat: {
|
|
247
|
+
currency,
|
|
248
|
+
value
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function readElementNumberAttribute(element, attribute) {
|
|
253
|
+
const raw = element.getAttribute(attribute);
|
|
254
|
+
if (raw === null || raw.trim() === "") return void 0;
|
|
255
|
+
const parsed = Number(raw);
|
|
256
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
257
|
+
}
|
|
258
|
+
function readElementAmountMsats(element) {
|
|
259
|
+
return readElementNumberAttribute(element, OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES2.amountMsats);
|
|
260
|
+
}
|
|
261
|
+
function readElementExpiresAt(element) {
|
|
262
|
+
return readElementNumberAttribute(element, OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES2.expiresAt);
|
|
263
|
+
}
|
|
264
|
+
function parseElementRail(value) {
|
|
265
|
+
if (value === "swap") return "swap";
|
|
266
|
+
if (value === "checkout_lock") return "checkout_lock";
|
|
267
|
+
return "lightning";
|
|
268
|
+
}
|
|
269
|
+
function showElementCopyFeedback(button) {
|
|
270
|
+
if (button === null) return;
|
|
271
|
+
let controller = elementCopyFeedbackControllers.get(button);
|
|
272
|
+
if (controller === void 0) {
|
|
273
|
+
const labelEl = button.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS.swapCopyLabel);
|
|
274
|
+
const resetValue = button.getAttribute("aria-label")?.trim() || labelEl?.textContent?.trim() || button.textContent?.trim() || checkoutLabels.copyInvoice;
|
|
275
|
+
controller = createTransientFeedbackController({
|
|
276
|
+
resetValue,
|
|
277
|
+
onValue: (label) => {
|
|
278
|
+
if (!button.isConnected) return;
|
|
279
|
+
if (button instanceof HTMLElement && button.hasAttribute("aria-label")) {
|
|
280
|
+
button.setAttribute("aria-label", label);
|
|
281
|
+
button.setAttribute("title", label);
|
|
282
|
+
}
|
|
283
|
+
if (labelEl instanceof HTMLElement) {
|
|
284
|
+
labelEl.textContent = label;
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
if (!(button instanceof HTMLElement && button.querySelector("svg"))) {
|
|
288
|
+
button.textContent = label;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
});
|
|
292
|
+
elementCopyFeedbackControllers.set(button, controller);
|
|
293
|
+
}
|
|
294
|
+
controller.show(checkoutLabels.copied);
|
|
295
|
+
}
|
|
296
|
+
function wireSwapSelectAllInputs(root) {
|
|
297
|
+
for (const input of root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS.swapSelectAll)) {
|
|
298
|
+
if (!(input instanceof HTMLInputElement)) continue;
|
|
299
|
+
const selectAll = () => {
|
|
300
|
+
input.select();
|
|
301
|
+
};
|
|
302
|
+
input.addEventListener("focus", selectAll);
|
|
303
|
+
input.addEventListener("click", selectAll);
|
|
304
|
+
input.addEventListener("mouseup", (event) => {
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
});
|
|
307
|
+
input.addEventListener("select", () => {
|
|
308
|
+
if (input.selectionStart !== 0 || input.selectionEnd !== input.value.length) {
|
|
309
|
+
input.setSelectionRange(0, input.value.length);
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function renderElementSwapCopyDetailHtml(label, value, options = {}) {
|
|
315
|
+
const displayValue = options.displayValue ?? value;
|
|
316
|
+
const link = options.href === void 0 ? options.kind === void 0 ? void 0 : createDetailExternalLink({
|
|
317
|
+
kind: options.kind,
|
|
318
|
+
value,
|
|
319
|
+
...options.payInAsset === void 0 ? {} : { payInAsset: options.payInAsset }
|
|
320
|
+
}) : {
|
|
321
|
+
href: options.href,
|
|
322
|
+
hrefLabel: options.hrefLabel ?? checkoutLabels.viewOnExplorer
|
|
323
|
+
};
|
|
324
|
+
const external = link === void 0 ? "" : `<a
|
|
325
|
+
part="swap-external"
|
|
326
|
+
class="${orClasses.swapDetailsLink}"
|
|
327
|
+
href="${escapeHtml(link.href)}"
|
|
328
|
+
rel="noreferrer"
|
|
329
|
+
target="_blank"
|
|
330
|
+
>${escapeHtml(link.hrefLabel)}</a>`;
|
|
331
|
+
const valueField = options.selectable === true ? `<input
|
|
332
|
+
class="${orClasses.swapDetailsInput}"
|
|
333
|
+
type="text"
|
|
334
|
+
readonly
|
|
335
|
+
value="${escapeHtml(displayValue)}"
|
|
336
|
+
aria-label="${escapeHtml(label)}"
|
|
337
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.swapSelectAll}
|
|
338
|
+
/>` : `<code class="${orClasses.swapDetailsCode}">${escapeHtml(displayValue)}</code>`;
|
|
339
|
+
return `
|
|
340
|
+
<dt class="${orClasses.swapDetailsDt}">${escapeHtml(label)}</dt>
|
|
341
|
+
<dd class="${orClasses.swapDetailsDd}">
|
|
342
|
+
${valueField}
|
|
343
|
+
<div class="${orClasses.swapDetailsActions}">
|
|
344
|
+
<button
|
|
345
|
+
part="swap-copy"
|
|
346
|
+
class="${orClasses.detailCopy}"
|
|
347
|
+
aria-label="Copy"
|
|
348
|
+
title="Copy"
|
|
349
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES.swapCopy}="${escapeHtml(value)}"
|
|
350
|
+
type="button"
|
|
351
|
+
>${COPY_INVOICE_ICON}</button>
|
|
352
|
+
${external}
|
|
353
|
+
</div>
|
|
354
|
+
</dd>
|
|
355
|
+
`;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/element-styles.ts
|
|
359
|
+
import { checkoutElementStyles } from "@openreceive/browser/headless";
|
|
360
|
+
var checkoutStyleTag = `<style>${checkoutElementStyles}</style>`;
|
|
361
|
+
var sheetsByDocument = /* @__PURE__ */ new WeakMap();
|
|
362
|
+
function adoptCheckoutStyles(root) {
|
|
363
|
+
const ownerDocument = root.ownerDocument;
|
|
364
|
+
if (ownerDocument === null) return false;
|
|
365
|
+
const view = ownerDocument.defaultView;
|
|
366
|
+
const SheetConstructor = view?.CSSStyleSheet ?? globalThis.CSSStyleSheet;
|
|
367
|
+
if (SheetConstructor === void 0 || !Array.isArray(root.adoptedStyleSheets)) return false;
|
|
368
|
+
let sheet = sheetsByDocument.get(ownerDocument);
|
|
369
|
+
if (sheet === void 0) {
|
|
370
|
+
try {
|
|
371
|
+
const created = new SheetConstructor();
|
|
372
|
+
if (typeof created.replaceSync !== "function") return false;
|
|
373
|
+
created.replaceSync(checkoutElementStyles);
|
|
374
|
+
sheet = created;
|
|
375
|
+
} catch {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
sheetsByDocument.set(ownerDocument, sheet);
|
|
379
|
+
}
|
|
380
|
+
if (!root.adoptedStyleSheets.includes(sheet)) {
|
|
381
|
+
root.adoptedStyleSheets = [...root.adoptedStyleSheets, sheet];
|
|
382
|
+
}
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// src/render-checkout.ts
|
|
387
|
+
import {
|
|
388
|
+
assertDisplayInvoice,
|
|
389
|
+
createCheckoutStatusModel,
|
|
390
|
+
createLightningInvoiceDecodeUrl as createLightningInvoiceDecodeUrl2,
|
|
391
|
+
createPaymentDataEntries,
|
|
392
|
+
deriveCheckoutStateLabels,
|
|
393
|
+
deriveStatus,
|
|
394
|
+
escapeHtml as escapeHtml6,
|
|
395
|
+
formatAmountCaption,
|
|
396
|
+
OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES,
|
|
397
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_PARTS,
|
|
398
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES6,
|
|
399
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_PARTS,
|
|
400
|
+
checkoutLabels as checkoutLabels6,
|
|
401
|
+
orClasses as orClasses6
|
|
402
|
+
} from "@openreceive/browser/headless";
|
|
403
|
+
|
|
404
|
+
// src/render-wizard.ts
|
|
405
|
+
import {
|
|
406
|
+
assetButtonClasses,
|
|
407
|
+
buildMethodGridEntries,
|
|
408
|
+
checkoutLabels as checkoutLabels5,
|
|
409
|
+
createPaymentWizardModel,
|
|
410
|
+
createSwapUnavailableModel,
|
|
411
|
+
createWizardRouteAssetDisplays,
|
|
412
|
+
createWizardRouteDisplays,
|
|
413
|
+
escapeHtml as escapeHtml5,
|
|
414
|
+
findSwapGridGroup,
|
|
415
|
+
formatChooseNetworkHeading,
|
|
416
|
+
formatNetworkSummary,
|
|
417
|
+
getNetworkIcon,
|
|
418
|
+
getPaymentMethodIcon,
|
|
419
|
+
getSwapOptionIcon,
|
|
420
|
+
getWizardEmptyMessage,
|
|
421
|
+
networkButtonClasses,
|
|
422
|
+
networkCheckClasses,
|
|
423
|
+
networkMobileRevealClasses,
|
|
424
|
+
networkSummaryIconClasses,
|
|
425
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5,
|
|
426
|
+
orClasses as orClasses5,
|
|
427
|
+
paymentAccentId,
|
|
428
|
+
paymentMethods,
|
|
429
|
+
swapAssetMatchesRoute as swapAssetMatchesRoute2,
|
|
430
|
+
swapGroupLimitOption,
|
|
431
|
+
swapPickerKey,
|
|
432
|
+
wizardNetworkGroupIds
|
|
433
|
+
} from "@openreceive/browser/headless";
|
|
434
|
+
|
|
435
|
+
// src/render-provider-tutorial.ts
|
|
436
|
+
import {
|
|
437
|
+
createLightningInvoiceDecodeUrl,
|
|
438
|
+
escapeHtml as escapeHtml2,
|
|
439
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2,
|
|
440
|
+
checkoutLabels as checkoutLabels2,
|
|
441
|
+
orClasses as orClasses2
|
|
442
|
+
} from "@openreceive/browser/headless";
|
|
443
|
+
function renderProviderOpenActionHtml(provider) {
|
|
444
|
+
if (provider.tutorials.length === 0) {
|
|
445
|
+
return `<a class="${orClasses2.providerOpen}" href="${escapeHtml2(provider.url)}" rel="noreferrer" target="_blank">${escapeHtml2(provider.openLabel)}</a>`;
|
|
446
|
+
}
|
|
447
|
+
return `
|
|
448
|
+
<button
|
|
449
|
+
part="provider-open"
|
|
450
|
+
class="${orClasses2.providerOpen}"
|
|
451
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorial}="${escapeHtml2(provider.id)}"
|
|
452
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorialIndex}="0"
|
|
453
|
+
type="button"
|
|
454
|
+
>${escapeHtml2(provider.openLabel)}</button>
|
|
455
|
+
`;
|
|
456
|
+
}
|
|
457
|
+
function renderTutorialModalHtml(routes, activeProviderId, activeTutorialIndex, copied, lightningInvoice, decodeLinkUrl) {
|
|
458
|
+
if (activeProviderId === null) return "";
|
|
459
|
+
const provider = routes.flatMap((route) => route.providers).find((candidate) => candidate.id === activeProviderId);
|
|
460
|
+
if (provider === void 0 || provider.tutorials.length === 0) return "";
|
|
461
|
+
const totalSteps = provider.tutorials.length + 1;
|
|
462
|
+
const stepIndex = Math.max(0, Math.min(provider.tutorials.length, activeTutorialIndex));
|
|
463
|
+
const tutorial = stepIndex === 0 ? void 0 : provider.tutorials[stepIndex - 1];
|
|
464
|
+
const previousIndex = Math.max(0, stepIndex - 1);
|
|
465
|
+
const nextIndex = Math.min(provider.tutorials.length, stepIndex + 1);
|
|
466
|
+
const isFinalStep = stepIndex === provider.tutorials.length;
|
|
467
|
+
const decodeHref = lightningInvoice === void 0 || lightningInvoice.trim() === "" ? void 0 : createLightningInvoiceDecodeUrl(lightningInvoice, decodeLinkUrl);
|
|
468
|
+
const decodeButton = decodeHref === void 0 ? "" : `<a part="tutorial-decode" class="${orClasses2.tutorialCopy}" href="${escapeHtml2(decodeHref)}" rel="noreferrer" target="_blank">${escapeHtml2(checkoutLabels2.decodeInvoice)}</a>`;
|
|
469
|
+
const body = stepIndex === 0 ? `
|
|
470
|
+
<div part="tutorial-intro" class="${orClasses2.tutorialIntro}">
|
|
471
|
+
<img part="tutorial-provider-logo" alt="" src="${escapeHtml2(provider.icon)}" class="${orClasses2.tutorialProviderLogo}">
|
|
472
|
+
<p>${escapeHtml2(checkoutLabels2.tutorialIntroPrefix)} ${escapeHtml2(provider.name)}.</p>
|
|
473
|
+
<p>${escapeHtml2(checkoutLabels2.tutorialIntroCopy)}</p>
|
|
474
|
+
<button part="tutorial-copy" class="${orClasses2.tutorialCopy}" type="button">${escapeHtml2(checkoutLabels2.copyInvoice)}</button>
|
|
475
|
+
${decodeButton}
|
|
476
|
+
${copied ? `<p part="tutorial-copy-message" class="${orClasses2.tutorialCopyMessage}">${escapeHtml2(checkoutLabels2.tutorialCopiedContinue)}</p>` : ""}
|
|
477
|
+
</div>
|
|
478
|
+
` : `
|
|
479
|
+
<div part="tutorial-frame" class="${orClasses2.tutorialFrame}">
|
|
480
|
+
<img part="tutorial-image" class="${orClasses2.tutorialImage}" alt="${escapeHtml2(tutorial?.caption ?? "")}" src="${escapeHtml2(tutorial?.image ?? "")}">
|
|
481
|
+
</div>
|
|
482
|
+
<p part="tutorial-caption" class="${orClasses2.tutorialCaption}">${escapeHtml2(tutorial?.caption ?? "")}</p>
|
|
483
|
+
`;
|
|
484
|
+
return `
|
|
485
|
+
<div part="tutorial" class="${orClasses2.tutorialModal}" role="dialog" aria-modal="true" aria-label="${escapeHtml2(checkoutLabels2.tutorialTitlePrefix)} ${escapeHtml2(provider.name)}" tabindex="-1">
|
|
486
|
+
<div part="tutorial-dialog" class="${orClasses2.tutorialBox}">
|
|
487
|
+
<div part="tutorial-header" class="${orClasses2.tutorialHeader}">
|
|
488
|
+
<div part="tutorial-title" class="${orClasses2.tutorialTitle}">
|
|
489
|
+
<img part="tutorial-header-logo" alt="" src="${escapeHtml2(provider.icon)}" class="${orClasses2.tutorialHeaderLogo}">
|
|
490
|
+
<h3>${escapeHtml2(checkoutLabels2.tutorialTitlePrefix)} ${escapeHtml2(provider.name)}</h3>
|
|
491
|
+
</div>
|
|
492
|
+
<button
|
|
493
|
+
part="tutorial-close"
|
|
494
|
+
class="${orClasses2.tutorialClose}"
|
|
495
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorial}=""
|
|
496
|
+
type="button"
|
|
497
|
+
aria-label="${escapeHtml2(checkoutLabels2.tutorialClose)}"
|
|
498
|
+
>X</button>
|
|
499
|
+
</div>
|
|
500
|
+
${body}
|
|
501
|
+
<div part="tutorial-steps" class="${orClasses2.tutorialSteps}" aria-hidden="true">
|
|
502
|
+
${Array.from(
|
|
503
|
+
{ length: totalSteps },
|
|
504
|
+
(_, index) => `
|
|
505
|
+
<span part="${index === stepIndex ? "tutorial-step-active" : "tutorial-step"}" class="${index === stepIndex ? orClasses2.tutorialStepActive : orClasses2.tutorialStep}"></span>
|
|
506
|
+
`
|
|
507
|
+
).join("")}
|
|
508
|
+
</div>
|
|
509
|
+
<p part="tutorial-progress" class="${orClasses2.tutorialProgress}">Step ${stepIndex + 1} of ${totalSteps}</p>
|
|
510
|
+
<div part="tutorial-controls" class="${orClasses2.tutorialControls}">
|
|
511
|
+
<button
|
|
512
|
+
part="tutorial-nav"
|
|
513
|
+
class="${orClasses2.btn}"
|
|
514
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorial}="${escapeHtml2(provider.id)}"
|
|
515
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorialIndex}="${previousIndex}"
|
|
516
|
+
type="button"
|
|
517
|
+
${stepIndex === 0 ? "disabled" : ""}
|
|
518
|
+
>${escapeHtml2(checkoutLabels2.tutorialBack)}</button>
|
|
519
|
+
<button
|
|
520
|
+
part="tutorial-nav"
|
|
521
|
+
class="${orClasses2.btn}"
|
|
522
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorial}="${isFinalStep ? "" : escapeHtml2(provider.id)}"
|
|
523
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES2.providerTutorialIndex}="${nextIndex}"
|
|
524
|
+
type="button"
|
|
525
|
+
>${escapeHtml2(isFinalStep ? checkoutLabels2.tutorialExit : checkoutLabels2.tutorialNext)}</button>
|
|
526
|
+
</div>
|
|
527
|
+
</div>
|
|
528
|
+
</div>
|
|
529
|
+
`;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// src/render-swap-panel.ts
|
|
533
|
+
import {
|
|
534
|
+
createSwapDisplayModel,
|
|
535
|
+
escapeHtml as escapeHtml4,
|
|
536
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4,
|
|
537
|
+
checkoutLabels as checkoutLabels4,
|
|
538
|
+
swapAssetMatchesRoute,
|
|
539
|
+
swapOptionLimitMessage,
|
|
540
|
+
orClasses as orClasses4
|
|
541
|
+
} from "@openreceive/browser/headless";
|
|
542
|
+
|
|
543
|
+
// src/transaction-details.ts
|
|
544
|
+
import {
|
|
545
|
+
escapeHtml as escapeHtml3,
|
|
546
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES as OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES3,
|
|
547
|
+
OPENRECEIVE_PAYMENT_WIZARD_SELECTORS as OPENRECEIVE_PAYMENT_WIZARD_SELECTORS2,
|
|
548
|
+
checkoutLabels as checkoutLabels3,
|
|
549
|
+
orClasses as orClasses3,
|
|
550
|
+
resolveTransactionDetailRows
|
|
551
|
+
} from "@openreceive/browser/headless";
|
|
552
|
+
function renderTransactionDetailsHtml(source, options = {}) {
|
|
553
|
+
const rows = resolveTransactionDetailRows(source);
|
|
554
|
+
if (rows.length === 0) return "";
|
|
555
|
+
const openAttr = options.open === true ? " open" : "";
|
|
556
|
+
const className = options.className ?? orClasses3.transactionDetails;
|
|
557
|
+
return `
|
|
558
|
+
<details part="transaction-details" class="${escapeHtml3(className)}"${openAttr}>
|
|
559
|
+
<summary class="${orClasses3.transactionDetailsTitle}">${escapeHtml3(checkoutLabels3.transactionDetails)}</summary>
|
|
560
|
+
<div class="${orClasses3.transactionDetailsContent}">
|
|
561
|
+
<dl part="swap-details" class="${orClasses3.swapDetails}">
|
|
562
|
+
${rows.map(
|
|
563
|
+
(row) => renderElementSwapCopyDetailHtml(row.label, row.copyValue ?? row.value, {
|
|
564
|
+
displayValue: row.value,
|
|
565
|
+
...row.href === void 0 ? {} : { href: row.href },
|
|
566
|
+
...row.hrefLabel === void 0 ? {} : { hrefLabel: row.hrefLabel }
|
|
567
|
+
})
|
|
568
|
+
).join("")}
|
|
569
|
+
</dl>
|
|
570
|
+
</div>
|
|
571
|
+
</details>
|
|
572
|
+
`;
|
|
573
|
+
}
|
|
574
|
+
function createTransactionDetailsElement(source, options = {}) {
|
|
575
|
+
const html = renderTransactionDetailsHtml(source, options);
|
|
576
|
+
if (html === "") return null;
|
|
577
|
+
const doc = options.document ?? globalThis.document;
|
|
578
|
+
const host = doc.createElement("div");
|
|
579
|
+
host.innerHTML = html.trim();
|
|
580
|
+
const details = host.firstElementChild;
|
|
581
|
+
if (!(details instanceof HTMLElement)) return null;
|
|
582
|
+
wireTransactionDetailsCopy(details, options.onCopyError);
|
|
583
|
+
return details;
|
|
584
|
+
}
|
|
585
|
+
function wireTransactionDetailsCopy(root, onCopyError) {
|
|
586
|
+
wireSwapSelectAllInputs(root);
|
|
587
|
+
for (const button of root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS2.swapCopy)) {
|
|
588
|
+
if (!(button instanceof HTMLButtonElement)) continue;
|
|
589
|
+
button.addEventListener("click", () => {
|
|
590
|
+
const value = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES3.swapCopy);
|
|
591
|
+
if (value === null || value === "") return;
|
|
592
|
+
void globalThis.navigator?.clipboard?.writeText(value).then(() => showElementCopyFeedback(button)).catch((error) => onCopyError?.(error));
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// src/render-swap-panel.ts
|
|
598
|
+
function renderElementSwapActionsHtml(routeKey, options, view) {
|
|
599
|
+
const shown = options.filter((option) => option.provider.length > 0).filter((option) => swapAssetMatchesRoute(routeKey, option.pay_in_asset));
|
|
600
|
+
if (shown.length === 0) return "";
|
|
601
|
+
return `
|
|
602
|
+
<div part="swap-actions" class="${orClasses4.swapActions}">
|
|
603
|
+
${shown.map((option) => {
|
|
604
|
+
const disabled = option.available === false;
|
|
605
|
+
const limitMessage = elementsSwapLimitMessage(option, view);
|
|
606
|
+
const info = disabled ? limitMessage === void 0 ? "" : `<p part="swap-warning" class="${orClasses4.swapWarning}">${escapeHtml4(limitMessage)}</p>` : option.pay_amount === void 0 ? "" : `<p part="swap-estimate" class="${orClasses4.swapEstimate}">Estimated ${escapeHtml4(option.pay_amount)} ${escapeHtml4(option.label)} to settle this checkout.</p>`;
|
|
607
|
+
return `
|
|
608
|
+
<div class="${orClasses4.swapAction}">
|
|
609
|
+
${info}
|
|
610
|
+
<button
|
|
611
|
+
part="swap-start"
|
|
612
|
+
class="${orClasses4.swapStart}"
|
|
613
|
+
${disabled ? "" : `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapStart}="${escapeHtml4(option.pay_in_asset)}"`}
|
|
614
|
+
${disabled ? 'disabled aria-disabled="true"' : ""}
|
|
615
|
+
type="button"
|
|
616
|
+
>${escapeHtml4(
|
|
617
|
+
checkoutLabels4.createPaymentAddress.replace("{asset}", option.label).replace("{network}", option.network_label)
|
|
618
|
+
)}</button>
|
|
619
|
+
</div>
|
|
620
|
+
`;
|
|
621
|
+
}).join("")}
|
|
622
|
+
</div>
|
|
623
|
+
`;
|
|
624
|
+
}
|
|
625
|
+
function elementsSwapLimitMessage(option, view) {
|
|
626
|
+
const checkout = view.amountMsats === void 0 ? void 0 : {
|
|
627
|
+
amount_msats: view.amountMsats,
|
|
628
|
+
...view.fiat?.currency === void 0 || view.fiat.value === void 0 ? {} : { fiat: { currency: view.fiat.currency, value: view.fiat.value } }
|
|
629
|
+
};
|
|
630
|
+
return swapOptionLimitMessage(option, checkout);
|
|
631
|
+
}
|
|
632
|
+
function renderElementSwapPanelHtml(invoice, view = {}) {
|
|
633
|
+
const display = createSwapDisplayModel(invoice);
|
|
634
|
+
if (display === void 0) return "";
|
|
635
|
+
const backButton = `
|
|
636
|
+
<button part="swap-back" class="${orClasses4.swapBack}" ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapBack} type="button">${escapeHtml4(checkoutLabels4.payWithLightningInstead)}</button>
|
|
637
|
+
`;
|
|
638
|
+
const supportDetails = renderElementSwapSupportDetailsHtml(display);
|
|
639
|
+
const heading = `
|
|
640
|
+
<div part="swap-heading" class="${orClasses4.swapHeading}">
|
|
641
|
+
<strong class="${orClasses4.swapHeadingTitle}">${escapeHtml4(display.providerStateLabel)}</strong>
|
|
642
|
+
<span class="${orClasses4.swapHeadingDetail}">${escapeHtml4(display.providerStateDetail)}</span>
|
|
643
|
+
</div>
|
|
644
|
+
`;
|
|
645
|
+
if (display.state === "creating") {
|
|
646
|
+
return `
|
|
647
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
648
|
+
<div part="status" class="${orClasses4.paymentStatus}">
|
|
649
|
+
<span part="spinner" class="${orClasses4.spinner}" aria-hidden="true"></span>
|
|
650
|
+
<div class="${orClasses4.paymentStatusBody}">
|
|
651
|
+
<strong class="${orClasses4.paymentStatusTitle}">${escapeHtml4(display.providerStateLabel)}</strong>
|
|
652
|
+
<p class="${orClasses4.paymentStatusDetail}">${escapeHtml4(display.providerStateDetail)}</p>
|
|
653
|
+
</div>
|
|
654
|
+
</div>
|
|
655
|
+
${backButton}
|
|
656
|
+
</section>
|
|
657
|
+
`;
|
|
658
|
+
}
|
|
659
|
+
if (display.state === "deposit") {
|
|
660
|
+
const feeBreakdown = renderElementSwapFeeBreakdownHtml(display.feeBreakdown);
|
|
661
|
+
const waitingStatus = `
|
|
662
|
+
<div part="status" class="${orClasses4.paymentStatus}">
|
|
663
|
+
<span part="spinner" class="${orClasses4.spinner}" aria-hidden="true"></span>
|
|
664
|
+
<div class="${orClasses4.paymentStatusBody}">
|
|
665
|
+
<div class="${orClasses4.swapWaitingTitle}">
|
|
666
|
+
<strong class="${orClasses4.paymentStatusTitle}">${escapeHtml4(display.providerStateLabel)}</strong>
|
|
667
|
+
<strong part="swap-countdown" class="${orClasses4.swapCountdown}">${escapeHtml4(display.countdownLabel)}</strong>
|
|
668
|
+
</div>
|
|
669
|
+
<p class="${orClasses4.paymentStatusDetail}">${escapeHtml4(display.providerStateDetail)}</p>
|
|
670
|
+
</div>
|
|
671
|
+
</div>
|
|
672
|
+
`;
|
|
673
|
+
return `
|
|
674
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
675
|
+
<p part="swap-instruction" class="${orClasses4.swapInstruction}">Pay <strong>${escapeHtml4(display.depositAmount)} ${escapeHtml4(display.assetLabel)}</strong> to this address</p>
|
|
676
|
+
${renderElementSwapNetworkWarningHtml(display)}
|
|
677
|
+
<div part="swap-deposit-layout" class="${orClasses4.swapDepositLayout}">
|
|
678
|
+
<div part="swap-qr" class="${orClasses4.swapQr}" ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapQr}="${escapeHtml4(display.qrPayload)}"></div>
|
|
679
|
+
<div part="swap-deposit-side" class="${orClasses4.swapDepositSide}">
|
|
680
|
+
<dl part="swap-details" class="${orClasses4.swapDetails}">
|
|
681
|
+
${renderElementSwapCopyDetailHtml("Address", display.depositAddress, {
|
|
682
|
+
selectable: true
|
|
683
|
+
})}
|
|
684
|
+
${display.depositMemo === void 0 ? "" : renderElementSwapCopyDetailHtml("Memo", display.depositMemo)}
|
|
685
|
+
${renderElementSwapCopyDetailHtml("Amount", display.depositAmount, { selectable: true })}
|
|
686
|
+
</dl>
|
|
687
|
+
${waitingStatus}
|
|
688
|
+
${feeBreakdown}
|
|
689
|
+
</div>
|
|
690
|
+
</div>
|
|
691
|
+
${backButton}
|
|
692
|
+
</section>
|
|
693
|
+
`;
|
|
694
|
+
}
|
|
695
|
+
if (display.state === "settled") {
|
|
696
|
+
return `
|
|
697
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
698
|
+
${heading}
|
|
699
|
+
<dl part="swap-details" class="${orClasses4.swapDetails}">
|
|
700
|
+
${display.depositTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Deposit transaction", display.depositTxId, { kind: "tx", payInAsset: display.payInAsset })}
|
|
701
|
+
${display.payoutTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Lightning payout", display.payoutTxId)}
|
|
702
|
+
${display.providerOrderId === void 0 ? "" : renderElementSwapCopyDetailHtml("Provider order", display.providerOrderId)}
|
|
703
|
+
</dl>
|
|
704
|
+
${renderElementTransactionDetailsHtml(invoice, view)}
|
|
705
|
+
</section>
|
|
706
|
+
`;
|
|
707
|
+
}
|
|
708
|
+
if (display.state === "progress") {
|
|
709
|
+
return `
|
|
710
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
711
|
+
<div part="status" class="${orClasses4.paymentStatus}">
|
|
712
|
+
<span part="spinner" class="${orClasses4.spinner}" aria-hidden="true"></span>
|
|
713
|
+
<div class="${orClasses4.paymentStatusBody}">
|
|
714
|
+
<strong class="${orClasses4.paymentStatusTitle}">${escapeHtml4(display.providerStateLabel)}</strong>
|
|
715
|
+
<p class="${orClasses4.paymentStatusDetail}">${escapeHtml4(display.providerStateDetail)}</p>
|
|
716
|
+
</div>
|
|
717
|
+
</div>
|
|
718
|
+
${supportDetails}
|
|
719
|
+
</section>
|
|
720
|
+
`;
|
|
721
|
+
}
|
|
722
|
+
if (display.state === "expired") {
|
|
723
|
+
return `
|
|
724
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
725
|
+
${heading}
|
|
726
|
+
<p part="swap-warning" class="${orClasses4.swapWarning}">This payment address expired without a detected payment. Create a new payment address to try again.</p>
|
|
727
|
+
${supportDetails}
|
|
728
|
+
${backButton}
|
|
729
|
+
</section>
|
|
730
|
+
`;
|
|
731
|
+
}
|
|
732
|
+
if (display.state === "refund_required") {
|
|
733
|
+
const stagedRefundAddress = display.refundAddress;
|
|
734
|
+
const refundFacts = renderElementSwapRefundFactsHtml(display);
|
|
735
|
+
return `
|
|
736
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
737
|
+
${heading}
|
|
738
|
+
${refundFacts}
|
|
739
|
+
<p part="swap-warning" class="${orClasses4.swapWarning}">Use a ${escapeHtml4(display.networkLabel)} address you control. Do not paste the deposit address.</p>
|
|
740
|
+
${stagedRefundAddress === void 0 ? `
|
|
741
|
+
<form
|
|
742
|
+
part="swap-refund"
|
|
743
|
+
class="${orClasses4.swapRefund}"
|
|
744
|
+
novalidate
|
|
745
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundForm}="${escapeHtml4(display.attemptId)}"
|
|
746
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundPayInAsset}="${escapeHtml4(display.payInAsset)}"
|
|
747
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundNetworkLabel}="${escapeHtml4(display.networkLabel)}"
|
|
748
|
+
${display.refundAllowed ? `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundAllowed}="true"` : ""}
|
|
749
|
+
>
|
|
750
|
+
<input
|
|
751
|
+
class="${orClasses4.swapRefundInput}"
|
|
752
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundAddress}
|
|
753
|
+
name="refund_address"
|
|
754
|
+
placeholder="${escapeHtml4(checkoutLabels4.refundAddressPlaceholder.replace("{network}", display.networkLabel))}"
|
|
755
|
+
type="text"
|
|
756
|
+
autocomplete="off"
|
|
757
|
+
required
|
|
758
|
+
>
|
|
759
|
+
<p
|
|
760
|
+
part="swap-refund-error"
|
|
761
|
+
class="${orClasses4.swapRefundError}"
|
|
762
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundError}
|
|
763
|
+
hidden
|
|
764
|
+
role="alert"
|
|
765
|
+
></p>
|
|
766
|
+
<p part="swap-refund-hint" class="${orClasses4.swapRefundHint}">Make sure you control this ${escapeHtml4(display.networkLabel)} address. Refunds sent to the wrong address usually cannot be recovered.</p>
|
|
767
|
+
<button class="${orClasses4.btn}" type="submit">${escapeHtml4(checkoutLabels4.reviewRefundAddress)}</button>
|
|
768
|
+
</form>
|
|
769
|
+
` : `
|
|
770
|
+
<form
|
|
771
|
+
part="swap-refund"
|
|
772
|
+
class="${orClasses4.swapRefund}"
|
|
773
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundForm}="${escapeHtml4(display.attemptId)}"
|
|
774
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundPayInAsset}="${escapeHtml4(display.payInAsset)}"
|
|
775
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundNetworkLabel}="${escapeHtml4(display.networkLabel)}"
|
|
776
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundConfirm}="true"
|
|
777
|
+
${display.refundAllowed ? `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundAllowed}="true"` : ""}
|
|
778
|
+
>
|
|
779
|
+
<p part="swap-warning" class="${orClasses4.swapWarning}">${escapeHtml4(checkoutLabels4.confirmRefundTo.replace("{address}", stagedRefundAddress))}</p>
|
|
780
|
+
<input
|
|
781
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES4.swapRefundAddress}
|
|
782
|
+
name="refund_address"
|
|
783
|
+
type="hidden"
|
|
784
|
+
value="${escapeHtml4(stagedRefundAddress)}"
|
|
785
|
+
>
|
|
786
|
+
<button class="${orClasses4.btn}" type="submit">${escapeHtml4(checkoutLabels4.confirmRefund)}</button>
|
|
787
|
+
</form>
|
|
788
|
+
`}
|
|
789
|
+
${supportDetails}
|
|
790
|
+
${renderElementSwapRefundReturnWarningHtml()}
|
|
791
|
+
</section>
|
|
792
|
+
`;
|
|
793
|
+
}
|
|
794
|
+
if (display.state === "refund_pending" || display.state === "refunded") {
|
|
795
|
+
const refundFacts = renderElementSwapRefundFactsHtml(display);
|
|
796
|
+
return `
|
|
797
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
798
|
+
${heading}
|
|
799
|
+
${refundFacts}
|
|
800
|
+
<dl part="swap-details" class="${orClasses4.swapDetails}">
|
|
801
|
+
${display.refundAddress === void 0 ? "" : renderElementSwapCopyDetailHtml("Refund address", display.refundAddress, { kind: "address", payInAsset: display.payInAsset })}
|
|
802
|
+
${display.refundTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Refund transaction", display.refundTxId, { kind: "tx", payInAsset: display.payInAsset })}
|
|
803
|
+
</dl>
|
|
804
|
+
${supportDetails}
|
|
805
|
+
${renderElementSwapRefundReturnWarningHtml()}
|
|
806
|
+
</section>
|
|
807
|
+
`;
|
|
808
|
+
}
|
|
809
|
+
return `
|
|
810
|
+
<section part="swap-panel" class="${orClasses4.swapPanel}">
|
|
811
|
+
${heading}
|
|
812
|
+
<p part="swap-warning" class="${orClasses4.swapWarning}">${escapeHtml4(checkoutLabels4.supportReviewNeeded)}</p>
|
|
813
|
+
${supportDetails}
|
|
814
|
+
${backButton}
|
|
815
|
+
</section>
|
|
816
|
+
`;
|
|
817
|
+
}
|
|
818
|
+
function renderElementSwapRefundReturnWarningHtml() {
|
|
819
|
+
return `
|
|
820
|
+
<p part="swap-refund-return" class="${orClasses4.swapWarning}">${escapeHtml4(checkoutLabels4.refundReturnWarning)}</p>
|
|
821
|
+
`;
|
|
822
|
+
}
|
|
823
|
+
function renderElementSwapRefundFactsHtml(display) {
|
|
824
|
+
const rows = [
|
|
825
|
+
display.depositReceivedAmount === void 0 ? "" : renderElementSwapCopyDetailHtml(
|
|
826
|
+
"Amount received",
|
|
827
|
+
`${display.depositReceivedAmount} ${display.assetLabel}`
|
|
828
|
+
),
|
|
829
|
+
display.depositReceivedAmount === void 0 ? "" : renderElementSwapCopyDetailHtml(
|
|
830
|
+
"Amount required",
|
|
831
|
+
`${display.depositAmount} ${display.assetLabel}`
|
|
832
|
+
),
|
|
833
|
+
display.refundAmount === void 0 ? "" : renderElementSwapCopyDetailHtml(
|
|
834
|
+
"Estimated refund",
|
|
835
|
+
`${display.refundAmount} ${display.assetLabel}`
|
|
836
|
+
)
|
|
837
|
+
].join("");
|
|
838
|
+
if (rows.length === 0) return "";
|
|
839
|
+
return `<dl part="swap-details" class="${orClasses4.swapDetails}">${rows}</dl>`;
|
|
840
|
+
}
|
|
841
|
+
function renderElementSwapNetworkWarningHtml(display) {
|
|
842
|
+
const emphasisStart = display.networkWarning.indexOf(display.networkWarningEmphasis);
|
|
843
|
+
const before = emphasisStart === -1 ? escapeHtml4(display.networkWarning) : escapeHtml4(display.networkWarning.slice(0, emphasisStart));
|
|
844
|
+
const after = emphasisStart === -1 ? "" : escapeHtml4(
|
|
845
|
+
display.networkWarning.slice(emphasisStart + display.networkWarningEmphasis.length)
|
|
846
|
+
);
|
|
847
|
+
const emphasis = emphasisStart === -1 ? "" : `<strong class="${orClasses4.swapNetworkWarningEmphasis}">${escapeHtml4(display.networkWarningEmphasis)}</strong>`;
|
|
848
|
+
return `
|
|
849
|
+
<div part="swap-network-warning" role="alert" class="${orClasses4.swapNetworkWarning}">
|
|
850
|
+
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="${orClasses4.swapNetworkWarningIcon}" aria-hidden="true">
|
|
851
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
|
|
852
|
+
</svg>
|
|
853
|
+
<div class="${orClasses4.swapNetworkWarningContent}">
|
|
854
|
+
<p class="${orClasses4.swapNetworkWarningTitle}">${escapeHtml4(display.networkWarningTitle)}</p>
|
|
855
|
+
<p class="${orClasses4.swapNetworkWarningBody}">${before}${emphasis}${after}</p>
|
|
856
|
+
</div>
|
|
857
|
+
</div>
|
|
858
|
+
`;
|
|
859
|
+
}
|
|
860
|
+
function renderElementTransactionDetailsHtml(invoice, view) {
|
|
861
|
+
const bolt11 = typeof invoice.invoice === "string" ? invoice.invoice : view.lightningInvoice;
|
|
862
|
+
return renderTransactionDetailsHtml({
|
|
863
|
+
...view.reference === void 0 ? {} : { reference: view.reference },
|
|
864
|
+
...view.checkoutId === void 0 ? {} : { checkout_id: view.checkoutId },
|
|
865
|
+
invoice_id: invoice.invoice_id,
|
|
866
|
+
...bolt11 === void 0 ? {} : { invoice: bolt11 },
|
|
867
|
+
rail: invoice.rail,
|
|
868
|
+
...invoice.payment_hash === void 0 ? view.paymentHash === void 0 ? {} : { payment_hash: view.paymentHash } : { payment_hash: invoice.payment_hash },
|
|
869
|
+
...invoice.amount_msats === void 0 ? view.amountMsats === void 0 ? {} : { amount_msats: view.amountMsats } : { amount_msats: invoice.amount_msats },
|
|
870
|
+
...invoice.fiat_quote === void 0 ? view.fiat?.currency === void 0 || view.fiat.value === void 0 ? {} : { fiat_quote: { fiat: { currency: view.fiat.currency, value: view.fiat.value } } } : { fiat_quote: invoice.fiat_quote },
|
|
871
|
+
...invoice.transaction_state === void 0 ? {} : { transaction_state: invoice.transaction_state },
|
|
872
|
+
...invoice.workflow_state === void 0 ? {} : { workflow_state: invoice.workflow_state },
|
|
873
|
+
...invoice.expires_at === void 0 ? {} : { expires_at: invoice.expires_at },
|
|
874
|
+
...invoice.settled_at === void 0 ? {} : { settled_at: invoice.settled_at },
|
|
875
|
+
...invoice.swap === void 0 ? {} : { swap: invoice.swap }
|
|
876
|
+
});
|
|
877
|
+
}
|
|
878
|
+
function renderElementSwapFeeBreakdownHtml(breakdown) {
|
|
879
|
+
if (breakdown === void 0) return "";
|
|
880
|
+
const feeValue = breakdown.feePercent === void 0 ? breakdown.fee : `${breakdown.fee} (${breakdown.feePercent})`;
|
|
881
|
+
return `
|
|
882
|
+
<div part="swap-breakdown" class="${orClasses4.swapBreakdown}">
|
|
883
|
+
<p part="swap-breakdown-title" class="${orClasses4.swapBreakdownTitle}">${escapeHtml4(checkoutLabels4.paymentBreakdown)}</p>
|
|
884
|
+
<dl part="swap-details" class="${orClasses4.swapBreakdownRows}">
|
|
885
|
+
<dt>${escapeHtml4(checkoutLabels4.cartTotal)}</dt>
|
|
886
|
+
<dd>${escapeHtml4(breakdown.cartTotal)}</dd>
|
|
887
|
+
<dt>${escapeHtml4(checkoutLabels4.youSend)}</dt>
|
|
888
|
+
<dd>${escapeHtml4(breakdown.youSend)}</dd>
|
|
889
|
+
<dt>${escapeHtml4(checkoutLabels4.swapAndNetworkFees)}</dt>
|
|
890
|
+
<dd>${escapeHtml4(feeValue)}</dd>
|
|
891
|
+
</dl>
|
|
892
|
+
</div>
|
|
893
|
+
`;
|
|
894
|
+
}
|
|
895
|
+
function renderElementSwapSupportDetailsHtml(display) {
|
|
896
|
+
if (display.depositTxId === void 0 && display.payoutTxId === void 0 && display.refundTxId === void 0 && display.providerOrderId === void 0) {
|
|
897
|
+
return "";
|
|
898
|
+
}
|
|
899
|
+
return `
|
|
900
|
+
<details part="swap-support" class="${orClasses4.swapSupport}">
|
|
901
|
+
<summary class="${orClasses4.swapSupportTitle}">Payment details</summary>
|
|
902
|
+
<div class="${orClasses4.swapSupportContent}">
|
|
903
|
+
<dl part="swap-details" class="${orClasses4.swapDetails}">
|
|
904
|
+
${display.depositTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Deposit transaction", display.depositTxId, { kind: "tx", payInAsset: display.payInAsset })}
|
|
905
|
+
${display.payoutTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Lightning payout", display.payoutTxId)}
|
|
906
|
+
${display.refundTxId === void 0 ? "" : renderElementSwapCopyDetailHtml("Refund transaction", display.refundTxId, { kind: "tx", payInAsset: display.payInAsset })}
|
|
907
|
+
${display.providerOrderId === void 0 ? "" : renderElementSwapCopyDetailHtml("Provider order", display.providerOrderId)}
|
|
908
|
+
</dl>
|
|
909
|
+
</div>
|
|
910
|
+
</details>
|
|
911
|
+
`;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
// src/render-wizard.ts
|
|
915
|
+
function wizardStartingAsset(view) {
|
|
916
|
+
const asset = view.startingSwapAsset;
|
|
917
|
+
return asset !== void 0 && asset !== null && asset.length > 0 ? asset : void 0;
|
|
918
|
+
}
|
|
919
|
+
function swapGroupIsStarting(group, startingAsset) {
|
|
920
|
+
return startingAsset !== void 0 && group.options.some((option) => option.pay_in_asset === startingAsset);
|
|
921
|
+
}
|
|
922
|
+
function renderElementSwapUnavailableHtml(quote, checkout) {
|
|
923
|
+
const model = createSwapUnavailableModel(quote, checkout);
|
|
924
|
+
return `<section part="swap-panel" class="${orClasses5.swapPanel}">
|
|
925
|
+
<div part="swap-heading" class="${orClasses5.swapHeading}">
|
|
926
|
+
<strong class="${orClasses5.swapHeadingTitle}">${escapeHtml5(model.title)}</strong>
|
|
927
|
+
</div>
|
|
928
|
+
<p part="swap-warning" class="${orClasses5.swapWarning}">${escapeHtml5(model.detail)}</p>
|
|
929
|
+
${model.range === void 0 ? "" : `<p class="${orClasses5.swapWarning}">${escapeHtml5(model.range)}</p>`}
|
|
930
|
+
<p class="${orClasses5.swapProgress}">${escapeHtml5(model.hint)}</p>
|
|
931
|
+
</section>`;
|
|
932
|
+
}
|
|
933
|
+
function renderPaymentWizardHtml(view = {}) {
|
|
934
|
+
const selection = {
|
|
935
|
+
selectedMethod: view.selectedMethod ?? null,
|
|
936
|
+
selectedBitcoinRoute: view.selectedBitcoinRoute ?? null
|
|
937
|
+
};
|
|
938
|
+
const model = createPaymentWizardModel(selection);
|
|
939
|
+
const { wizard } = model;
|
|
940
|
+
const routeAssetDisplays = createWizardRouteAssetDisplays(model.routeAssets, {
|
|
941
|
+
selectedRoute: model.selectedRoute
|
|
942
|
+
});
|
|
943
|
+
const routeDisplays = createWizardRouteDisplays(wizard.routes);
|
|
944
|
+
const showRoutePicker = routeAssetDisplays.length > 0 && (model.selectedRoute === null || routeDisplays.length === 0);
|
|
945
|
+
const breadcrumbs = selection.selectedMethod === null ? "" : renderWizardBreadcrumbsHtml({
|
|
946
|
+
method: selection.selectedMethod,
|
|
947
|
+
selectedRoute: model.selectedRoute,
|
|
948
|
+
routeAssets: routeAssetDisplays
|
|
949
|
+
});
|
|
950
|
+
const swapAssetOptions = (view.swapOptions ?? []).filter((option) => option.provider.length > 0);
|
|
951
|
+
const selectedSwapAsset = view.selectedSwapAsset ?? null;
|
|
952
|
+
const selectedSwapOption = selectedSwapAsset === null ? void 0 : swapAssetOptions.find((option) => option.pay_in_asset === selectedSwapAsset);
|
|
953
|
+
if (selectedSwapAsset !== null) {
|
|
954
|
+
const label = selectedSwapOption === void 0 ? "this coin" : `${selectedSwapOption.label} \xB7 ${selectedSwapOption.network_label}`;
|
|
955
|
+
const activeSwap = view.swapInvoice !== void 0 && view.swapInvoice.swap?.pay_in_asset === selectedSwapAsset ? view.swapInvoice : void 0;
|
|
956
|
+
return `
|
|
957
|
+
<section part="wizard" class="${orClasses5.wizard}" ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.root}>
|
|
958
|
+
<div class="${orClasses5.wizardBody}">
|
|
959
|
+
<div part="wizard-breadcrumbs" class="${orClasses5.breadcrumbs}">
|
|
960
|
+
<button
|
|
961
|
+
part="wizard-breadcrumb"
|
|
962
|
+
class="${orClasses5.btnGhost}"
|
|
963
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.breadcrumb}="swap-asset"
|
|
964
|
+
type="button"
|
|
965
|
+
>${escapeHtml5(checkoutLabels5.switchPaymentMethod)}</button>
|
|
966
|
+
<span aria-hidden="true">/</span>
|
|
967
|
+
<span part="wizard-breadcrumb-current" class="${orClasses5.breadcrumbCurrent}">${escapeHtml5(label)}</span>
|
|
968
|
+
</div>
|
|
969
|
+
<div part="wizard-results" class="${orClasses5.wizardResults}">
|
|
970
|
+
${activeSwap !== void 0 ? renderElementSwapPanelHtml(activeSwap, view) : view.swapStartError !== void 0 ? `<section part="swap-panel" class="${orClasses5.swapPanel}">
|
|
971
|
+
<div part="swap-error" role="alert" class="${orClasses5.paymentStatus}">
|
|
972
|
+
<div class="${orClasses5.paymentStatusBody}">
|
|
973
|
+
<strong class="${orClasses5.paymentStatusTitle}">${escapeHtml5(checkoutLabels5.swapStartFailedTitle)}</strong>
|
|
974
|
+
<p class="${orClasses5.paymentStatusDetail}">${escapeHtml5(view.swapStartError)}</p>
|
|
975
|
+
<button
|
|
976
|
+
part="swap-retry"
|
|
977
|
+
class="${orClasses5.btn}"
|
|
978
|
+
type="button"
|
|
979
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapStart}="${escapeHtml5(selectedSwapAsset)}"
|
|
980
|
+
>${escapeHtml5(checkoutLabels5.tryAgain)}</button>
|
|
981
|
+
</div>
|
|
982
|
+
</div>
|
|
983
|
+
</section>` : view.unavailableSwapQuote !== void 0 ? renderElementSwapUnavailableHtml(
|
|
984
|
+
view.unavailableSwapQuote,
|
|
985
|
+
view.swapLimitContext
|
|
986
|
+
) : `<section part="swap-panel" class="${orClasses5.swapPanel}">
|
|
987
|
+
<div part="status" class="${orClasses5.paymentStatus}">
|
|
988
|
+
<span part="spinner" class="${orClasses5.spinner}" aria-hidden="true"></span>
|
|
989
|
+
<div class="${orClasses5.paymentStatusBody}">
|
|
990
|
+
<strong class="${orClasses5.paymentStatusTitle}">${escapeHtml5(checkoutLabels5.preparingPaymentAddress)}</strong>
|
|
991
|
+
<p class="${orClasses5.paymentStatusDetail}">${escapeHtml5(
|
|
992
|
+
checkoutLabels5.preparingPaymentAddressDetail.replace(
|
|
993
|
+
"{asset}",
|
|
994
|
+
selectedSwapOption?.label ?? "coin"
|
|
995
|
+
)
|
|
996
|
+
)}</p>
|
|
997
|
+
</div>
|
|
998
|
+
</div>
|
|
999
|
+
</section>`}
|
|
1000
|
+
</div>
|
|
1001
|
+
</div>
|
|
1002
|
+
</section>
|
|
1003
|
+
`;
|
|
1004
|
+
}
|
|
1005
|
+
const methodPicker = selection.selectedMethod === null ? renderElementCompactPaymentSelectorHtml(swapAssetOptions, view) : "";
|
|
1006
|
+
return `
|
|
1007
|
+
<section part="wizard" class="${orClasses5.wizard}" ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.root}>
|
|
1008
|
+
${view.wizardError === void 0 ? "" : `<p part="wizard-error" role="alert" class="${orClasses5.paymentStatusDetail}">${escapeHtml5(view.wizardError)}</p>`}
|
|
1009
|
+
${methodPicker}
|
|
1010
|
+
${selection.selectedMethod === null ? "" : `
|
|
1011
|
+
<div class="${orClasses5.wizardBody}">
|
|
1012
|
+
${breadcrumbs}
|
|
1013
|
+
${showRoutePicker ? `
|
|
1014
|
+
<div part="route-picker" class="${orClasses5.routePicker}">
|
|
1015
|
+
${routeAssetDisplays.map(
|
|
1016
|
+
(asset) => `
|
|
1017
|
+
<button
|
|
1018
|
+
part="route${asset.selected ? " selected" : ""}"
|
|
1019
|
+
class="${asset.selected ? orClasses5.routeButtonSelected : orClasses5.routeButton}"
|
|
1020
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.route}="${escapeHtml5(asset.id)}"
|
|
1021
|
+
type="button"
|
|
1022
|
+
>
|
|
1023
|
+
<img class="${orClasses5.methodIcon}" alt="" src="${escapeHtml5(asset.icon)}">
|
|
1024
|
+
<strong class="${orClasses5.methodTitle}">${escapeHtml5(asset.label)}</strong>
|
|
1025
|
+
<small class="${orClasses5.methodDetail}">${escapeHtml5(asset.subtitle)}</small>
|
|
1026
|
+
</button>
|
|
1027
|
+
`
|
|
1028
|
+
).join("")}
|
|
1029
|
+
</div>
|
|
1030
|
+
` : ""}
|
|
1031
|
+
${`
|
|
1032
|
+
<div part="wizard-results" class="${orClasses5.wizardResults}">
|
|
1033
|
+
${routeDisplays.length === 0 ? `
|
|
1034
|
+
<p part="wizard-empty" class="${orClasses5.wizardEmpty}">${escapeHtml5(
|
|
1035
|
+
getWizardEmptyMessage()
|
|
1036
|
+
)}</p>
|
|
1037
|
+
` : routeDisplays.map((route) => {
|
|
1038
|
+
const activeSwap = view.swapInvoice !== void 0 && swapAssetMatchesRoute2(route.key, view.swapInvoice.swap?.pay_in_asset) ? view.swapInvoice : void 0;
|
|
1039
|
+
return `
|
|
1040
|
+
<section part="wizard-route" class="${orClasses5.wizardRoute}">
|
|
1041
|
+
<h3 class="${orClasses5.wizardRouteHeading}">
|
|
1042
|
+
${escapeHtml5(route.title)}
|
|
1043
|
+
</h3>
|
|
1044
|
+
${activeSwap === void 0 ? renderElementSwapActionsHtml(route.key, view.swapOptions ?? [], view) : renderElementSwapPanelHtml(activeSwap, view)}
|
|
1045
|
+
${activeSwap === void 0 ? `<div part="provider-grid" class="${orClasses5.providerGrid}">
|
|
1046
|
+
${route.providers.map(
|
|
1047
|
+
(provider) => `
|
|
1048
|
+
<article part="provider" class="${orClasses5.providerCard}">
|
|
1049
|
+
<div part="provider-heading" class="${orClasses5.providerHeading}">
|
|
1050
|
+
<img class="${orClasses5.providerIcon}" alt="" src="${escapeHtml5(provider.icon)}">
|
|
1051
|
+
<h4 class="${orClasses5.providerName}">${escapeHtml5(provider.name)}</h4>
|
|
1052
|
+
</div>
|
|
1053
|
+
<p part="provider-kind" class="${orClasses5.providerKind}">${escapeHtml5(provider.kind)}</p>
|
|
1054
|
+
<div part="provider-actions" class="${orClasses5.providerActions}">
|
|
1055
|
+
${renderProviderOpenActionHtml(provider)}
|
|
1056
|
+
</div>
|
|
1057
|
+
</article>
|
|
1058
|
+
`
|
|
1059
|
+
).join("")}
|
|
1060
|
+
</div>` : ""}
|
|
1061
|
+
</section>
|
|
1062
|
+
`;
|
|
1063
|
+
}).join("")}
|
|
1064
|
+
</div>
|
|
1065
|
+
`}
|
|
1066
|
+
</div>
|
|
1067
|
+
`}
|
|
1068
|
+
${renderTutorialModalHtml(
|
|
1069
|
+
routeDisplays,
|
|
1070
|
+
view.activeTutorialProviderId ?? null,
|
|
1071
|
+
view.activeTutorialIndex ?? 0,
|
|
1072
|
+
view.activeTutorialCopied ?? false,
|
|
1073
|
+
view.lightningInvoice,
|
|
1074
|
+
view.decodeLinkUrl
|
|
1075
|
+
)}
|
|
1076
|
+
</section>
|
|
1077
|
+
`;
|
|
1078
|
+
}
|
|
1079
|
+
function renderElementCompactPaymentSelectorHtml(swapAssetOptions, view) {
|
|
1080
|
+
const entries = buildMethodGridEntries(paymentMethods, swapAssetOptions);
|
|
1081
|
+
const currenciesLoading = view.currenciesLoading === true && swapAssetOptions.length === 0;
|
|
1082
|
+
const selectedKey = view.selectedPickerKey ?? null;
|
|
1083
|
+
const selectedGroup = findSwapGridGroup(entries, selectedKey);
|
|
1084
|
+
const networkRequired = selectedGroup !== void 0 && selectedGroup.options.length > 1;
|
|
1085
|
+
const selectedNetworks = view.selectedSwapNetworks ?? {};
|
|
1086
|
+
const selectedGroupKey = selectedGroup?.label.trim().toUpperCase();
|
|
1087
|
+
const selectedNetworkAsset = selectedGroupKey === void 0 ? void 0 : selectedNetworks[selectedGroupKey];
|
|
1088
|
+
const selectedNetworkOption = selectedGroup === void 0 || selectedNetworkAsset === void 0 ? void 0 : selectedGroup.options.find((option) => option.pay_in_asset === selectedNetworkAsset);
|
|
1089
|
+
const startingAsset = wizardStartingAsset(view);
|
|
1090
|
+
const gridBusy = startingAsset !== void 0;
|
|
1091
|
+
let continueDisabled = selectedNetworkOption === void 0 || gridBusy;
|
|
1092
|
+
let continueAttr = "";
|
|
1093
|
+
let continueLabel = escapeHtml5(checkoutLabels5.continue);
|
|
1094
|
+
const continueStarting = selectedNetworkOption !== void 0 && selectedNetworkOption.pay_in_asset === startingAsset;
|
|
1095
|
+
if (selectedNetworkOption !== void 0) {
|
|
1096
|
+
const disabled = selectedNetworkOption.available === false;
|
|
1097
|
+
const limitMessage = elementsSwapLimitMessage(selectedNetworkOption, view);
|
|
1098
|
+
continueDisabled = disabled || gridBusy;
|
|
1099
|
+
continueAttr = disabled || gridBusy ? "" : `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapStart}="${escapeHtml5(selectedNetworkOption.pay_in_asset)}"`;
|
|
1100
|
+
if (disabled && limitMessage !== void 0) continueLabel = escapeHtml5(limitMessage);
|
|
1101
|
+
else if (continueStarting) continueLabel = escapeHtml5(checkoutLabels5.preparingPayment);
|
|
1102
|
+
} else if (networkRequired) {
|
|
1103
|
+
continueDisabled = true;
|
|
1104
|
+
}
|
|
1105
|
+
const continueButton = (className) => renderElementMethodConfirmHtml({
|
|
1106
|
+
className,
|
|
1107
|
+
disabled: continueDisabled,
|
|
1108
|
+
starting: continueStarting,
|
|
1109
|
+
swapStartAsset: continueAttr.length === 0 || selectedNetworkOption === void 0 ? void 0 : selectedNetworkOption.pay_in_asset,
|
|
1110
|
+
label: continueLabel
|
|
1111
|
+
});
|
|
1112
|
+
const tiles = entries.map((entry) => {
|
|
1113
|
+
if (entry.kind === "method") {
|
|
1114
|
+
const method = entry.method;
|
|
1115
|
+
const accent = paymentAccentId(method.id);
|
|
1116
|
+
return `
|
|
1117
|
+
<button
|
|
1118
|
+
part="method"
|
|
1119
|
+
type="button"
|
|
1120
|
+
class="${assetButtonClasses({ accent, selected: false, disabled: gridBusy })}"
|
|
1121
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.method}="${escapeHtml5(method.id)}"
|
|
1122
|
+
${gridBusy ? 'disabled aria-disabled="true"' : ""}
|
|
1123
|
+
>
|
|
1124
|
+
<span aria-hidden="true" class="${orClasses5.methodIconWrap}">
|
|
1125
|
+
<img class="${orClasses5.methodIcon}" alt="" src="${escapeHtml5(getPaymentMethodIcon(method.id))}">
|
|
1126
|
+
</span>
|
|
1127
|
+
<span class="${orClasses5.methodTitleWrap}">
|
|
1128
|
+
<span class="${orClasses5.methodTitle}">${escapeHtml5(method.title)}</span>
|
|
1129
|
+
</span>
|
|
1130
|
+
</button>`;
|
|
1131
|
+
}
|
|
1132
|
+
return renderElementSwapMethodGroupHtml(
|
|
1133
|
+
entry.group,
|
|
1134
|
+
view,
|
|
1135
|
+
selectedKey,
|
|
1136
|
+
selectedKey === swapPickerKey(entry.group.label) ? continueButton(orClasses5.methodConfirmDesktop) : void 0
|
|
1137
|
+
);
|
|
1138
|
+
}).join("");
|
|
1139
|
+
const loadingTile = currenciesLoading ? `<div part="currencies-loading" role="status" aria-live="polite" class="${orClasses5.methodCurrenciesLoading}">
|
|
1140
|
+
<span part="spinner" class="${orClasses5.spinner}" aria-hidden="true"></span>
|
|
1141
|
+
<span class="${orClasses5.methodTitle}">${escapeHtml5(checkoutLabels5.loadingCurrencies)}</span>
|
|
1142
|
+
</div>` : "";
|
|
1143
|
+
const desktopReveal = networkRequired && selectedGroup !== void 0 ? `<div class="${orClasses5.methodNetworkRevealDesktop}">${renderElementNetworkSelectorHtml(
|
|
1144
|
+
selectedGroup,
|
|
1145
|
+
view,
|
|
1146
|
+
continueButton(orClasses5.methodConfirmDesktop),
|
|
1147
|
+
false
|
|
1148
|
+
)}</div>` : "";
|
|
1149
|
+
return `
|
|
1150
|
+
<header class="${orClasses5.wizardHeader}">
|
|
1151
|
+
<h2 id="payment-method-heading" class="${orClasses5.wizardHeaderTitle}">${escapeHtml5(checkoutLabels5.wizardTitle)}</h2>
|
|
1152
|
+
<p class="${orClasses5.wizardHeaderSubtitle}">${escapeHtml5(checkoutLabels5.wizardSubtitle)}</p>
|
|
1153
|
+
</header>
|
|
1154
|
+
<div class="${orClasses5.wizardBody}" aria-labelledby="payment-method-heading">
|
|
1155
|
+
<div part="method-grid" role="group" aria-label="${escapeHtml5(checkoutLabels5.paymentMethod)}" class="${orClasses5.methodGrid}">
|
|
1156
|
+
${tiles}${loadingTile}
|
|
1157
|
+
</div>
|
|
1158
|
+
${desktopReveal}
|
|
1159
|
+
</div>
|
|
1160
|
+
`;
|
|
1161
|
+
}
|
|
1162
|
+
function renderElementNetworkSelectorHtml(group, view, continueButtonHtml, mobile) {
|
|
1163
|
+
const accent = paymentAccentId(group.label);
|
|
1164
|
+
const groupKey = group.label.trim().toUpperCase();
|
|
1165
|
+
const selectedNetworks = view.selectedSwapNetworks ?? {};
|
|
1166
|
+
const selectedAsset = selectedNetworks[groupKey];
|
|
1167
|
+
const selectedOption = selectedAsset === void 0 ? void 0 : group.options.find((option) => option.pay_in_asset === selectedAsset);
|
|
1168
|
+
const { panelId, headingId } = wizardNetworkGroupIds(groupKey);
|
|
1169
|
+
const networkButtons = group.options.map((option) => {
|
|
1170
|
+
const optionDisabled = option.available === false;
|
|
1171
|
+
const optionSelected = option.pay_in_asset === selectedOption?.pay_in_asset;
|
|
1172
|
+
const optionLimit = elementsSwapLimitMessage(option, view);
|
|
1173
|
+
return `
|
|
1174
|
+
<div class="${orClasses5.methodTile}">
|
|
1175
|
+
<button
|
|
1176
|
+
type="button"
|
|
1177
|
+
aria-pressed="${optionSelected ? "true" : "false"}"
|
|
1178
|
+
class="${networkButtonClasses({
|
|
1179
|
+
accent,
|
|
1180
|
+
selected: optionSelected,
|
|
1181
|
+
disabled: optionDisabled
|
|
1182
|
+
})}"
|
|
1183
|
+
${optionDisabled ? 'disabled aria-disabled="true"' : ""}
|
|
1184
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapNetwork}="${escapeHtml5(groupKey)}"
|
|
1185
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapNetworkValue}="${escapeHtml5(option.pay_in_asset)}"
|
|
1186
|
+
>
|
|
1187
|
+
<span aria-hidden="true" class="grid size-6 shrink-0 place-items-center">
|
|
1188
|
+
<img class="${orClasses5.methodNetworkIcon}" alt="" src="${escapeHtml5(getNetworkIcon(option.network_label))}">
|
|
1189
|
+
</span>
|
|
1190
|
+
<span class="truncate">${escapeHtml5(option.network_label)}</span>
|
|
1191
|
+
${optionSelected ? `<span aria-hidden="true" class="${networkCheckClasses(accent)}">\u2713</span>` : ""}
|
|
1192
|
+
</button>
|
|
1193
|
+
${optionDisabled && optionLimit !== void 0 ? `<span class="${orClasses5.methodLimitHint}">${escapeHtml5(optionLimit)}</span>` : ""}
|
|
1194
|
+
</div>`;
|
|
1195
|
+
}).join("");
|
|
1196
|
+
const summary = selectedOption === void 0 ? "" : `<p aria-live="polite" class="${orClasses5.methodNetworkSummary}">
|
|
1197
|
+
<span aria-hidden="true" class="${networkSummaryIconClasses(accent)}">\u2713</span>
|
|
1198
|
+
${escapeHtml5(formatNetworkSummary(group.label, selectedOption.network_label))}
|
|
1199
|
+
</p>`;
|
|
1200
|
+
return `
|
|
1201
|
+
<div
|
|
1202
|
+
id="${panelId}"
|
|
1203
|
+
role="group"
|
|
1204
|
+
aria-labelledby="${headingId}"
|
|
1205
|
+
class="${mobile ? networkMobileRevealClasses(accent) : orClasses5.methodNetworkReveal}"
|
|
1206
|
+
>
|
|
1207
|
+
<div class="${orClasses5.methodNetworkLayout}">
|
|
1208
|
+
<div>
|
|
1209
|
+
<h3 id="${headingId}" class="${orClasses5.methodNetworkHeading}">${escapeHtml5(
|
|
1210
|
+
formatChooseNetworkHeading(group.label)
|
|
1211
|
+
)}</h3>
|
|
1212
|
+
<p class="${orClasses5.methodNetworkHint}">${escapeHtml5(
|
|
1213
|
+
checkoutLabels5.selectNetworkToContinue
|
|
1214
|
+
)}</p>
|
|
1215
|
+
</div>
|
|
1216
|
+
<div role="group" aria-labelledby="${headingId}" class="${orClasses5.methodNetworkGrid}">
|
|
1217
|
+
${networkButtons}
|
|
1218
|
+
</div>
|
|
1219
|
+
${continueButtonHtml}
|
|
1220
|
+
</div>
|
|
1221
|
+
${summary}
|
|
1222
|
+
</div>`;
|
|
1223
|
+
}
|
|
1224
|
+
function renderElementMethodConfirmHtml(options) {
|
|
1225
|
+
const swapStart = options.disabled || options.swapStartAsset === void 0 ? "" : `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapStart}="${escapeHtml5(options.swapStartAsset)}"`;
|
|
1226
|
+
return `
|
|
1227
|
+
<button
|
|
1228
|
+
type="button"
|
|
1229
|
+
part="method-confirm"
|
|
1230
|
+
class="${options.className}"
|
|
1231
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.pickerContinue}=""
|
|
1232
|
+
${swapStart}
|
|
1233
|
+
${options.disabled ? 'disabled aria-disabled="true"' : ""}
|
|
1234
|
+
${options.starting ? 'aria-busy="true"' : ""}
|
|
1235
|
+
>${options.starting ? `<span part="spinner" class="${orClasses5.continueSpinner}" aria-hidden="true"></span>` : ""}${options.label}</button>`;
|
|
1236
|
+
}
|
|
1237
|
+
function renderElementSwapMethodGroupHtml(group, view, selectedKey, continueButtonHtml) {
|
|
1238
|
+
const selectedNetworks = view.selectedSwapNetworks ?? {};
|
|
1239
|
+
const groupKey = group.label.trim().toUpperCase();
|
|
1240
|
+
const pickerKey = swapPickerKey(group.label);
|
|
1241
|
+
const selected = selectedKey === pickerKey;
|
|
1242
|
+
const displayOption = group.options.find((option) => option.available !== false) ?? group.options[0];
|
|
1243
|
+
if (displayOption === void 0) return "";
|
|
1244
|
+
const multiNetwork = group.options.length > 1;
|
|
1245
|
+
const selectedAsset = selectedNetworks[groupKey];
|
|
1246
|
+
const selectedOption = selectedAsset === void 0 ? void 0 : group.options.find((option) => option.pay_in_asset === selectedAsset);
|
|
1247
|
+
const activeOption = selectedOption ?? displayOption;
|
|
1248
|
+
const startingAsset = wizardStartingAsset(view);
|
|
1249
|
+
const starting = swapGroupIsStarting(group, startingAsset);
|
|
1250
|
+
const gridBusy = startingAsset !== void 0;
|
|
1251
|
+
const disabled = group.options.every((option) => option.available === false);
|
|
1252
|
+
const accent = paymentAccentId(group.label);
|
|
1253
|
+
const limitOption = disabled ? swapGroupLimitOption(group.options) ?? activeOption : activeOption;
|
|
1254
|
+
const limitMessage = elementsSwapLimitMessage(limitOption, view);
|
|
1255
|
+
const { panelId } = wizardNetworkGroupIds(groupKey);
|
|
1256
|
+
const networkDetail = !disabled && multiNetwork ? selected && selectedOption !== void 0 ? `${escapeHtml5(selectedOption.network_label)} network` : escapeHtml5(checkoutLabels5.selectNetwork) : void 0;
|
|
1257
|
+
const mobileReveal = multiNetwork ? `
|
|
1258
|
+
<div class="${orClasses5.methodNetworkRevealAnim} ${selected ? orClasses5.methodNetworkRevealAnimOpen : orClasses5.methodNetworkRevealAnimClosed}">
|
|
1259
|
+
<div class="${orClasses5.methodNetworkRevealInner}">
|
|
1260
|
+
${selected ? renderElementNetworkSelectorHtml(group, view, continueButtonHtml ?? "", true) : ""}
|
|
1261
|
+
</div>
|
|
1262
|
+
</div>` : "";
|
|
1263
|
+
return `
|
|
1264
|
+
<div class="${orClasses5.methodTile}">
|
|
1265
|
+
<button
|
|
1266
|
+
part="method"
|
|
1267
|
+
type="button"
|
|
1268
|
+
aria-pressed="${starting || multiNetwork && selected ? "true" : "false"}"
|
|
1269
|
+
${multiNetwork ? `aria-expanded="${selected ? "true" : "false"}" aria-controls="${panelId}"` : ""}
|
|
1270
|
+
class="${assetButtonClasses({
|
|
1271
|
+
accent,
|
|
1272
|
+
selected: starting || multiNetwork && selected,
|
|
1273
|
+
disabled: disabled || gridBusy && !starting
|
|
1274
|
+
})}"
|
|
1275
|
+
${disabled || gridBusy ? 'disabled aria-disabled="true"' : ""}
|
|
1276
|
+
${starting ? 'aria-busy="true"' : ""}
|
|
1277
|
+
${disabled || gridBusy ? "" : multiNetwork ? `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.pickerSelect}="${escapeHtml5(pickerKey)}"` : `${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.swapStart}="${escapeHtml5(displayOption.pay_in_asset)}"`}
|
|
1278
|
+
>
|
|
1279
|
+
<span aria-hidden="true" class="${orClasses5.methodIconWrap}">
|
|
1280
|
+
${starting ? `<span part="spinner" class="${orClasses5.spinner}" aria-hidden="true"></span>` : `<img class="${orClasses5.methodIcon}" alt="" src="${escapeHtml5(getSwapOptionIcon(displayOption))}">`}
|
|
1281
|
+
</span>
|
|
1282
|
+
<span class="${orClasses5.methodTitleWrap}">
|
|
1283
|
+
<span class="${orClasses5.methodTitle}">${escapeHtml5(group.label)}</span>
|
|
1284
|
+
${networkDetail === void 0 ? "" : `<span class="${orClasses5.methodDetailMobile}">${networkDetail}</span>`}
|
|
1285
|
+
</span>
|
|
1286
|
+
</button>
|
|
1287
|
+
${disabled && limitMessage !== void 0 ? `<span class="${orClasses5.methodLimitHint}">${escapeHtml5(limitMessage)}</span>` : ""}
|
|
1288
|
+
${mobileReveal}
|
|
1289
|
+
</div>`;
|
|
1290
|
+
}
|
|
1291
|
+
function renderWizardBreadcrumbsHtml(options) {
|
|
1292
|
+
const method = paymentMethods.find((candidate) => candidate.id === options.method);
|
|
1293
|
+
const methodLabel = method?.title ?? checkoutLabels5.paymentMethod;
|
|
1294
|
+
const routeLabel = options.selectedRoute === null || options.routeAssets.length <= 1 ? null : options.routeAssets.find((asset) => asset.id === options.selectedRoute)?.label ?? options.selectedRoute;
|
|
1295
|
+
return `
|
|
1296
|
+
<nav part="wizard-breadcrumbs" class="${orClasses5.breadcrumbs}" aria-label="Payment path">
|
|
1297
|
+
<button
|
|
1298
|
+
part="wizard-breadcrumb"
|
|
1299
|
+
class="${orClasses5.btnGhost}"
|
|
1300
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.breadcrumb}="method"
|
|
1301
|
+
type="button"
|
|
1302
|
+
>
|
|
1303
|
+
<span>${escapeHtml5(checkoutLabels5.switchPaymentMethod)}</span>
|
|
1304
|
+
</button>
|
|
1305
|
+
<span part="wizard-breadcrumb-separator" aria-hidden="true">/</span>
|
|
1306
|
+
${routeLabel === null ? `<span part="wizard-breadcrumb-current" class="${orClasses5.breadcrumbCurrent}">${escapeHtml5(methodLabel)}</span>` : `
|
|
1307
|
+
<button
|
|
1308
|
+
part="wizard-breadcrumb"
|
|
1309
|
+
class="${orClasses5.btnGhost}"
|
|
1310
|
+
${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES5.breadcrumb}="route"
|
|
1311
|
+
type="button"
|
|
1312
|
+
>
|
|
1313
|
+
<span>${escapeHtml5(methodLabel)}</span>
|
|
1314
|
+
</button>
|
|
1315
|
+
<span part="wizard-breadcrumb-separator" aria-hidden="true">/</span>
|
|
1316
|
+
<span part="wizard-breadcrumb-current" class="${orClasses5.breadcrumbCurrent}">${escapeHtml5(routeLabel)}</span>
|
|
1317
|
+
`}
|
|
1318
|
+
</nav>
|
|
1319
|
+
`;
|
|
1320
|
+
}
|
|
1321
|
+
|
|
1322
|
+
// src/views.ts
|
|
1323
|
+
import {
|
|
1324
|
+
createCheckoutSnapshotFromInvoice,
|
|
1325
|
+
createCheckoutState
|
|
1326
|
+
} from "@openreceive/browser/headless";
|
|
1327
|
+
function parseElementInvoiceId(value) {
|
|
1328
|
+
if (value === null || value.trim() === "") return void 0;
|
|
1329
|
+
return value;
|
|
1330
|
+
}
|
|
1331
|
+
function createElementCheckoutState(view) {
|
|
1332
|
+
const invoiceId = parseElementInvoiceId(view.invoice_id ?? null);
|
|
1333
|
+
if (invoiceId === void 0) return void 0;
|
|
1334
|
+
return createCheckoutState(
|
|
1335
|
+
createCheckoutSnapshotFromInvoice({
|
|
1336
|
+
invoice_id: invoiceId,
|
|
1337
|
+
invoice: view.invoice,
|
|
1338
|
+
rail: view.rail ?? "lightning",
|
|
1339
|
+
...view.payment_hash === void 0 ? {} : { payment_hash: view.payment_hash },
|
|
1340
|
+
...view.amount_msats === void 0 ? {} : { amount_msats: view.amount_msats },
|
|
1341
|
+
...view.fiat_quote === void 0 ? {} : { fiat_quote: view.fiat_quote },
|
|
1342
|
+
...view.status === void 0 ? {} : { transaction_state: transactionStateFromStatus(view.status) },
|
|
1343
|
+
...view.expires_at === void 0 ? {} : { expires_at: view.expires_at }
|
|
1344
|
+
})
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
function transactionStateFromStatus(status) {
|
|
1348
|
+
if (status === "settled") return "settled";
|
|
1349
|
+
if (status === "expired") return "expired";
|
|
1350
|
+
if (status === "failed") return "failed";
|
|
1351
|
+
return "pending";
|
|
1352
|
+
}
|
|
1353
|
+
function parseElementStatus(value) {
|
|
1354
|
+
if (value === "pending" || value === "settled" || value === "expired" || value === "failed") {
|
|
1355
|
+
return value;
|
|
1356
|
+
}
|
|
1357
|
+
return void 0;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
// src/render-checkout.ts
|
|
1361
|
+
function renderCheckoutHtml(view) {
|
|
1362
|
+
if (view.invoice !== "") assertDisplayInvoice(view.invoice);
|
|
1363
|
+
const checkoutState = view.liveState ?? createElementCheckoutState(view);
|
|
1364
|
+
const labels = deriveCheckoutStateLabels(view);
|
|
1365
|
+
const amountCaption = formatAmountCaption({
|
|
1366
|
+
amountLabel: labels.amountLabel,
|
|
1367
|
+
fiatLabel: labels.fiatLabel,
|
|
1368
|
+
fiatCurrency: view.fiat_quote?.fiat?.currency
|
|
1369
|
+
});
|
|
1370
|
+
const satsDetail = amountCaption === void 0 ? "" : `<div part="sats-detail" class="${orClasses6.satsDetail}">${escapeHtml6(amountCaption)}</div>`;
|
|
1371
|
+
const statusLabel = view.status ?? (checkoutState === void 0 ? deriveStatus(view) : deriveStatus(checkoutState));
|
|
1372
|
+
const showSummaryMeta = statusLabel === "settled" || statusLabel === "expired";
|
|
1373
|
+
const stateClass = statusLabel === "settled" ? orClasses6.stateSettled : orClasses6.statePending;
|
|
1374
|
+
const stateLabel = showSummaryMeta ? `<span part="state" class="${stateClass}" data-state="${escapeHtml6(statusLabel)}">${escapeHtml6(statusLabel)}</span>` : "";
|
|
1375
|
+
const status = checkoutState === void 0 ? "" : renderElementPaymentStatusHtml(checkoutState);
|
|
1376
|
+
const statusModel = checkoutState === void 0 ? void 0 : createCheckoutStatusModel(checkoutState);
|
|
1377
|
+
const expired = statusModel?.phase === "expired";
|
|
1378
|
+
const settled = statusLabel === "settled";
|
|
1379
|
+
const swapFocused = (view.wizard?.selectedSwapAsset ?? null) !== null;
|
|
1380
|
+
const hideLightning = !settled && (view.lightningRequested === false || swapFocused && !expired);
|
|
1381
|
+
const wizard = expired && !swapFocused || settled || view.payment_wizard === false ? "" : renderPaymentWizardHtml(view.wizard);
|
|
1382
|
+
const copyButton = `<button part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.copy}" class="${orClasses6.btn}" type="button">${COPY_INVOICE_ICON}<span ${OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES6.swapCopyLabel}>${escapeHtml6(checkoutLabels6.copyInvoice)}</span></button>`;
|
|
1383
|
+
const decodeInvoice = view.invoice.trim() !== "" ? view.invoice : typeof view.wizard?.lightningInvoice === "string" && view.wizard.lightningInvoice.trim() !== "" ? view.wizard.lightningInvoice : void 0;
|
|
1384
|
+
const decodeHref = decodeInvoice === void 0 ? void 0 : createLightningInvoiceDecodeUrl2(decodeInvoice, view.decodeLinkUrl);
|
|
1385
|
+
const decodeButton = decodeHref === void 0 ? "" : `<a part="decode-invoice" class="${orClasses6.btn}" href="${escapeHtml6(decodeHref)}" rel="noreferrer" target="_blank">${escapeHtml6(checkoutLabels6.decodeInvoice)}</a>`;
|
|
1386
|
+
const startOverButton = `<button part="${OPENRECEIVE_CHECKOUT_ELEMENT_PARTS.startOver}" class="${orClasses6.btn}" type="button">${escapeHtml6(checkoutLabels6.startOver)}</button>`;
|
|
1387
|
+
const lightningPane = hideLightning || expired || settled ? "" : `<div part="lightning-pane" class="${orClasses6.lightningPane}">
|
|
1388
|
+
<div part="qr" class="${orClasses6.qr}" ${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.qr}></div>
|
|
1389
|
+
${satsDetail}
|
|
1390
|
+
</div>`;
|
|
1391
|
+
const paymentLayoutClass = expired || settled ? orClasses6.paymentLayoutExpired : orClasses6.paymentLayout;
|
|
1392
|
+
const metaRow = stateLabel === "" ? "" : `<div part="meta" class="${orClasses6.meta}">${stateLabel}</div>`;
|
|
1393
|
+
const invoiceTitle = expired || settled ? "" : `<p part="invoice-title" class="${orClasses6.invoiceTitle}">${escapeHtml6(checkoutLabels6.bitcoinLightningInvoice)}</p>`;
|
|
1394
|
+
const actions = settled ? (
|
|
1395
|
+
// No state means no attempt to derive one from (a standalone caller that
|
|
1396
|
+
// passed `status: "settled"` and no invoice-id), so the panel reads the
|
|
1397
|
+
// attributes directly.
|
|
1398
|
+
renderElementPaymentDataHtml(checkoutState ?? { ...view, rail: view.rail ?? "lightning" })
|
|
1399
|
+
) : expired ? `<div part="actions" class="${orClasses6.actions}">${startOverButton}</div>` : `<div part="actions" class="${orClasses6.actions}">${copyButton}${decodeButton}</div>`;
|
|
1400
|
+
const resolvedTheme = view.theme ?? "light";
|
|
1401
|
+
return `
|
|
1402
|
+
${styleTag(view.inlineStyles)}
|
|
1403
|
+
<section part="root" data-theme="${escapeHtml6(resolvedTheme)}" class="${orClasses6.root}">
|
|
1404
|
+
${hideLightning ? "" : `<div part="payment-layout" class="${paymentLayoutClass}">
|
|
1405
|
+
${lightningPane}
|
|
1406
|
+
<div part="payment-info" class="${orClasses6.paymentInfo}">
|
|
1407
|
+
${invoiceTitle}
|
|
1408
|
+
${status}
|
|
1409
|
+
${metaRow}
|
|
1410
|
+
${actions}
|
|
1411
|
+
</div>
|
|
1412
|
+
</div>`}
|
|
1413
|
+
${wizard}
|
|
1414
|
+
</section>
|
|
1415
|
+
`;
|
|
1416
|
+
}
|
|
1417
|
+
function styleTag(inlineStyles) {
|
|
1418
|
+
return inlineStyles === false ? "" : checkoutStyleTag;
|
|
1419
|
+
}
|
|
1420
|
+
function renderCheckoutCreateErrorHtml(message, options = {}) {
|
|
1421
|
+
const resolvedTheme = options.theme ?? "light";
|
|
1422
|
+
const retryButton = `<button part="retry" class="${orClasses6.btn}" type="button">Try again</button>`;
|
|
1423
|
+
return `
|
|
1424
|
+
${styleTag(options.inlineStyles)}
|
|
1425
|
+
<section part="root" data-theme="${escapeHtml6(resolvedTheme)}" class="${orClasses6.root}" data-openreceive-create-error>
|
|
1426
|
+
<div part="status" role="alert" class="${orClasses6.creating}">
|
|
1427
|
+
<div><strong>Could not start checkout.</strong></div>
|
|
1428
|
+
<p>${escapeHtml6(message)}</p>
|
|
1429
|
+
${retryButton}
|
|
1430
|
+
</div>
|
|
1431
|
+
</section>
|
|
1432
|
+
`;
|
|
1433
|
+
}
|
|
1434
|
+
function renderCheckoutCreatingHtml(options = {}) {
|
|
1435
|
+
const resolvedTheme = options.theme ?? "light";
|
|
1436
|
+
return `
|
|
1437
|
+
${styleTag(options.inlineStyles)}
|
|
1438
|
+
<section part="root" data-theme="${escapeHtml6(resolvedTheme)}" class="${orClasses6.root}" data-openreceive-creating>
|
|
1439
|
+
<div part="status" class="${orClasses6.creating}">
|
|
1440
|
+
<span part="spinner" class="${orClasses6.spinner}" aria-hidden="true"></span>
|
|
1441
|
+
<div><strong>Creating checkout\u2026</strong></div>
|
|
1442
|
+
</div>
|
|
1443
|
+
</section>
|
|
1444
|
+
`;
|
|
1445
|
+
}
|
|
1446
|
+
function renderThemeToggleHtml(label, options = {}) {
|
|
1447
|
+
return `
|
|
1448
|
+
${styleTag(options.inlineStyles)}
|
|
1449
|
+
<button
|
|
1450
|
+
aria-label="${escapeHtml6(label)}"
|
|
1451
|
+
class="${orClasses6.themeToggle}"
|
|
1452
|
+
part="${OPENRECEIVE_THEME_TOGGLE_ELEMENT_PARTS.button}"
|
|
1453
|
+
title="${escapeHtml6(label)}"
|
|
1454
|
+
type="button"
|
|
1455
|
+
${OPENRECEIVE_CHECKOUT_DATA_ATTRIBUTES.themeToggle}
|
|
1456
|
+
>${escapeHtml6(label)}</button>
|
|
1457
|
+
`;
|
|
1458
|
+
}
|
|
1459
|
+
var SETTLED_CHECK_ICON = `<svg part="settled-icon" class="${orClasses6.settledIcon}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" aria-hidden="true" focusable="false"><path stroke-linecap="round" stroke-linejoin="round" d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>`;
|
|
1460
|
+
function renderElementPaymentStatusHtml(state) {
|
|
1461
|
+
const status = createCheckoutStatusModel(state);
|
|
1462
|
+
const countdown = status.countdownLabel === void 0 ? "" : `<small part="countdown" class="${orClasses6.countdown}">${escapeHtml6(status.countdownPrefix)} <strong class="${orClasses6.countdownStrong}">${escapeHtml6(status.countdownLabel)}</strong></small>`;
|
|
1463
|
+
return `
|
|
1464
|
+
<div part="status" role="status" aria-live="polite" class="${orClasses6.paymentStatus}">
|
|
1465
|
+
${status.waiting ? `<span part="spinner" class="${orClasses6.spinner}" aria-hidden="true"></span>` : ""}
|
|
1466
|
+
${status.phase === "settled" ? SETTLED_CHECK_ICON : ""}
|
|
1467
|
+
<div class="${orClasses6.paymentStatusBody}">
|
|
1468
|
+
<strong class="${orClasses6.paymentStatusTitle}">${escapeHtml6(status.title)}</strong>
|
|
1469
|
+
<p class="${orClasses6.paymentStatusDetail}">${escapeHtml6(status.detail)}</p>
|
|
1470
|
+
${countdown}
|
|
1471
|
+
</div>
|
|
1472
|
+
</div>
|
|
1473
|
+
`;
|
|
1474
|
+
}
|
|
1475
|
+
function renderElementPaymentDataHtml(source) {
|
|
1476
|
+
const entries = createPaymentDataEntries(source);
|
|
1477
|
+
if (entries.length === 0) return "";
|
|
1478
|
+
const rows = entries.map(
|
|
1479
|
+
(entry) => `
|
|
1480
|
+
<div part="payment-data-row" class="${orClasses6.paymentDataRow}">
|
|
1481
|
+
<span class="${orClasses6.paymentDataKey}">${escapeHtml6(entry.label)}</span>
|
|
1482
|
+
<p class="${orClasses6.paymentDataValue}">${escapeHtml6(entry.value)}</p>
|
|
1483
|
+
</div>`
|
|
1484
|
+
).join("");
|
|
1485
|
+
return `
|
|
1486
|
+
<details part="payment-data" class="${orClasses6.paymentData}">
|
|
1487
|
+
<summary part="payment-data-summary" class="${orClasses6.paymentDataTitle}">${escapeHtml6(checkoutLabels6.viewPaymentData)}</summary>
|
|
1488
|
+
<div class="${orClasses6.paymentDataBody}">${rows}</div>
|
|
1489
|
+
</details>
|
|
1490
|
+
`;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
// src/define-elements.ts
|
|
1494
|
+
var DEFAULT_TAG_NAME = OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME;
|
|
1495
|
+
function markElementConfirmButtonBusy(button) {
|
|
1496
|
+
button.disabled = true;
|
|
1497
|
+
button.setAttribute("aria-disabled", "true");
|
|
1498
|
+
button.setAttribute("aria-busy", "true");
|
|
1499
|
+
if (button.querySelector('[part="spinner"]') !== null) return;
|
|
1500
|
+
const spinner = button.ownerDocument.createElement("span");
|
|
1501
|
+
spinner.setAttribute("part", "spinner");
|
|
1502
|
+
spinner.className = orClasses7.continueSpinner;
|
|
1503
|
+
spinner.setAttribute("aria-hidden", "true");
|
|
1504
|
+
button.prepend(spinner);
|
|
1505
|
+
}
|
|
1506
|
+
function checkoutSnapshotDisplayKey(snapshot) {
|
|
1507
|
+
const { active: _active, paid_at: _paidAt, invoices, ...display } = snapshot;
|
|
1508
|
+
return JSON.stringify({
|
|
1509
|
+
...display,
|
|
1510
|
+
invoices: invoices.map(
|
|
1511
|
+
({ amount_msats: _amountMsats, fiat_quote: _fiatQuote, ...invoice }) => invoice
|
|
1512
|
+
)
|
|
1513
|
+
});
|
|
1514
|
+
}
|
|
1515
|
+
function defineElements(options = {}) {
|
|
1516
|
+
const registry = options.registry ?? globalThis.customElements;
|
|
1517
|
+
const HTMLElementCtor = globalThis.HTMLElement;
|
|
1518
|
+
const tagName = options.tagName ?? DEFAULT_TAG_NAME;
|
|
1519
|
+
const themeToggleTagName = options.themeToggleTagName ?? OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME;
|
|
1520
|
+
if (registry === void 0 || HTMLElementCtor === void 0) {
|
|
1521
|
+
throw new Error("Custom elements are unavailable in this environment.");
|
|
1522
|
+
}
|
|
1523
|
+
class CheckoutElement extends HTMLElementCtor {
|
|
1524
|
+
selection = createPaymentWizardSelection();
|
|
1525
|
+
activeTutorialProviderId = null;
|
|
1526
|
+
activeTutorialIndex = 0;
|
|
1527
|
+
activeTutorialCopied = false;
|
|
1528
|
+
swapOptions = [];
|
|
1529
|
+
swapOptionsLoaded = false;
|
|
1530
|
+
selectedSwapNetworks = {};
|
|
1531
|
+
selectedPickerKey = null;
|
|
1532
|
+
selectedSwapAsset = null;
|
|
1533
|
+
startedSwapInvoice;
|
|
1534
|
+
latestCheckoutSnapshot;
|
|
1535
|
+
/** Skip full shadow rebuilds when a poll returns the same payable UI. */
|
|
1536
|
+
lastSnapshotDisplayKey;
|
|
1537
|
+
dismissedSwapInvoiceId = null;
|
|
1538
|
+
controller;
|
|
1539
|
+
announcedSettledReference;
|
|
1540
|
+
/** Last applied state, used to detect countdown-only ticks (partial DOM update). */
|
|
1541
|
+
lastCheckoutState;
|
|
1542
|
+
/** Refund-address draft survives poll rebuilds of the shadow tree. */
|
|
1543
|
+
refundAddressDraft = "";
|
|
1544
|
+
refundAddressDraftFocused = false;
|
|
1545
|
+
refundAddressDraftSelectionStart = null;
|
|
1546
|
+
refundAddressDraftSelectionEnd = null;
|
|
1547
|
+
/** Tutorial provider whose dialog was last focused, so re-renders don't re-steal focus. */
|
|
1548
|
+
focusedTutorialProviderId = null;
|
|
1549
|
+
/**
|
|
1550
|
+
* Create mode's whole request lifecycle — prepare-once, the deferred
|
|
1551
|
+
* Lightning mint, the swap start — plus the guard that keeps attributes the
|
|
1552
|
+
* element writes itself out of attributeChangedCallback. Every one of those
|
|
1553
|
+
* is a double-POST guard, which is why they live together.
|
|
1554
|
+
*/
|
|
1555
|
+
session = createElementCheckoutSession({
|
|
1556
|
+
element: this,
|
|
1557
|
+
logger: options.logger,
|
|
1558
|
+
swapSelection: {
|
|
1559
|
+
started: () => this.startedSwapInvoice,
|
|
1560
|
+
setStarted: (invoice) => {
|
|
1561
|
+
this.startedSwapInvoice = invoice;
|
|
1562
|
+
},
|
|
1563
|
+
dismissedInvoiceId: () => this.dismissedSwapInvoiceId,
|
|
1564
|
+
setDismissedInvoiceId: (invoiceId) => {
|
|
1565
|
+
this.dismissedSwapInvoiceId = invoiceId;
|
|
1566
|
+
},
|
|
1567
|
+
setSelectedAsset: (payInAsset) => {
|
|
1568
|
+
this.selectedSwapAsset = payInAsset;
|
|
1569
|
+
}
|
|
1570
|
+
},
|
|
1571
|
+
isCreateMode: () => this.isCreateMode(),
|
|
1572
|
+
render: () => {
|
|
1573
|
+
this.render();
|
|
1574
|
+
},
|
|
1575
|
+
startCheckoutController: () => {
|
|
1576
|
+
this.startCheckoutController();
|
|
1577
|
+
},
|
|
1578
|
+
handleControllerSnapshot: (snapshot) => {
|
|
1579
|
+
this.handleControllerSnapshot(snapshot);
|
|
1580
|
+
},
|
|
1581
|
+
latestCheckoutSnapshot: () => this.latestCheckoutSnapshot,
|
|
1582
|
+
currentCheckoutSnapshot: () => this.currentCheckoutSnapshot(),
|
|
1583
|
+
currentThemeOption: () => this.currentThemeOption(),
|
|
1584
|
+
createMetadata: () => this.createMetadata(),
|
|
1585
|
+
syncResumePath: (reference) => {
|
|
1586
|
+
this.syncResumePath(reference);
|
|
1587
|
+
},
|
|
1588
|
+
resolvePollPrefix: (reference) => this.resolvePollPrefix(reference),
|
|
1589
|
+
dispatchError: (error) => {
|
|
1590
|
+
this.dispatchError(error);
|
|
1591
|
+
}
|
|
1592
|
+
});
|
|
1593
|
+
/** When `theme` is unset, follow the nearest ancestor `[data-theme]` (e.g. ThemeScope). */
|
|
1594
|
+
themeAncestorObserver;
|
|
1595
|
+
observedThemeAncestor = null;
|
|
1596
|
+
static get observedAttributes() {
|
|
1597
|
+
return [
|
|
1598
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference,
|
|
1599
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix,
|
|
1600
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoiceId,
|
|
1601
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice,
|
|
1602
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.rail,
|
|
1603
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentHash,
|
|
1604
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.amountMsats,
|
|
1605
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.fiatCurrency,
|
|
1606
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.fiatValue,
|
|
1607
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.status,
|
|
1608
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.expiresAt,
|
|
1609
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme,
|
|
1610
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentWizard,
|
|
1611
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.syncUrl,
|
|
1612
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.resumePathPrefix,
|
|
1613
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.routeReference,
|
|
1614
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.polling,
|
|
1615
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.pollIntervalMs,
|
|
1616
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.decodeLinkUrl
|
|
1617
|
+
];
|
|
1618
|
+
}
|
|
1619
|
+
connectedCallback() {
|
|
1620
|
+
this.render();
|
|
1621
|
+
this.syncThemeAncestorObserver();
|
|
1622
|
+
if (this.isCreateMode()) {
|
|
1623
|
+
void this.session.createCheckout();
|
|
1624
|
+
return;
|
|
1625
|
+
}
|
|
1626
|
+
this.startCheckoutController();
|
|
1627
|
+
}
|
|
1628
|
+
attributeChangedCallback(name, oldValue, newValue) {
|
|
1629
|
+
if (!this.isConnected || this.session.applyingOwnAttributes) return;
|
|
1630
|
+
if (oldValue === newValue) return;
|
|
1631
|
+
const displayOnly = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.syncUrl || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.resumePathPrefix || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.routeReference || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.decodeLinkUrl;
|
|
1632
|
+
if (displayOnly) {
|
|
1633
|
+
this.render();
|
|
1634
|
+
this.syncThemeAncestorObserver();
|
|
1635
|
+
return;
|
|
1636
|
+
}
|
|
1637
|
+
const pollingChanged = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.polling || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.pollIntervalMs;
|
|
1638
|
+
if (pollingChanged) {
|
|
1639
|
+
this.render();
|
|
1640
|
+
this.syncThemeAncestorObserver();
|
|
1641
|
+
this.startCheckoutController();
|
|
1642
|
+
return;
|
|
1643
|
+
}
|
|
1644
|
+
const createInputChanged = name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix || name === OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice;
|
|
1645
|
+
if (createInputChanged) {
|
|
1646
|
+
this.session.forgetCreateKey();
|
|
1647
|
+
}
|
|
1648
|
+
if (this.isCreateMode()) {
|
|
1649
|
+
this.render();
|
|
1650
|
+
this.syncThemeAncestorObserver();
|
|
1651
|
+
if (createInputChanged) {
|
|
1652
|
+
void this.session.createCheckout();
|
|
1653
|
+
}
|
|
1654
|
+
return;
|
|
1655
|
+
}
|
|
1656
|
+
this.render();
|
|
1657
|
+
this.syncThemeAncestorObserver();
|
|
1658
|
+
this.startCheckoutController();
|
|
1659
|
+
}
|
|
1660
|
+
disconnectedCallback() {
|
|
1661
|
+
this.stopCheckoutController();
|
|
1662
|
+
this.stopThemeAncestorObserver();
|
|
1663
|
+
}
|
|
1664
|
+
// Create mode: a `reference` is set but no `invoice` snapshot is provided. The element
|
|
1665
|
+
// owns the whole lifecycle — it prepares the checkout against
|
|
1666
|
+
// `${prefix}/checkouts/prepare` (locking the amount without minting), mints a Lightning
|
|
1667
|
+
// invoice only when the payer picks Bitcoin, then polls status against
|
|
1668
|
+
// `${prefix}/payments/check` and drives swaps under the prefix.
|
|
1669
|
+
isCreateMode() {
|
|
1670
|
+
const invoice = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice);
|
|
1671
|
+
const reference = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference);
|
|
1672
|
+
return (invoice === null || invoice === "") && reference !== null && reference.length > 0;
|
|
1673
|
+
}
|
|
1674
|
+
/**
|
|
1675
|
+
* Create-time metadata from the JSON `metadata` attribute.
|
|
1676
|
+
*
|
|
1677
|
+
* Read lazily, at mint time — which is why `metadata` is deliberately NOT a
|
|
1678
|
+
* create input: a host that changes it after mount is picked up by the next
|
|
1679
|
+
* mint, and re-keying the prepared checkout for it would orphan the
|
|
1680
|
+
* prepared attempt for nothing.
|
|
1681
|
+
*
|
|
1682
|
+
* Host-authored like `poll-interval-ms`, and ruled the same way: a value
|
|
1683
|
+
* that is not a JSON object is a host typo and must be heard, not silently
|
|
1684
|
+
* dropped from the create request.
|
|
1685
|
+
*/
|
|
1686
|
+
createMetadata() {
|
|
1687
|
+
const raw = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.metadata);
|
|
1688
|
+
if (raw === null || raw.length === 0) return void 0;
|
|
1689
|
+
let parsed;
|
|
1690
|
+
try {
|
|
1691
|
+
parsed = JSON.parse(raw);
|
|
1692
|
+
} catch (error) {
|
|
1693
|
+
throw new TypeError(
|
|
1694
|
+
`metadata must be a JSON object: ${error instanceof Error ? error.message : String(error)}`
|
|
1695
|
+
);
|
|
1696
|
+
}
|
|
1697
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1698
|
+
throw new TypeError("metadata must be a JSON object");
|
|
1699
|
+
}
|
|
1700
|
+
return parsed;
|
|
1701
|
+
}
|
|
1702
|
+
/** Order data belongs to the host; this only performs optional History API sync. */
|
|
1703
|
+
syncResumePath(reference) {
|
|
1704
|
+
const syncUrl = parseBooleanAttribute(
|
|
1705
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.syncUrl)
|
|
1706
|
+
);
|
|
1707
|
+
if (syncUrl) {
|
|
1708
|
+
const resumePathPrefix = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.resumePathPrefix) ?? "/checkout";
|
|
1709
|
+
const routeReference = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.routeReference) ?? void 0;
|
|
1710
|
+
enterCheckoutResumePath(reference, {
|
|
1711
|
+
pathPrefix: resumePathPrefix,
|
|
1712
|
+
...routeReference === void 0 || routeReference.length === 0 ? {} : { routeReference }
|
|
1713
|
+
});
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
handleControllerSnapshot(snapshot) {
|
|
1717
|
+
this.latestCheckoutSnapshot = snapshot;
|
|
1718
|
+
if (snapshot.payment_methods !== void 0) {
|
|
1719
|
+
this.swapOptions = snapshot.payment_methods;
|
|
1720
|
+
this.swapOptionsLoaded = true;
|
|
1721
|
+
}
|
|
1722
|
+
const swapInvoice = snapshot.invoices.find(
|
|
1723
|
+
(invoice) => invoice.rail === "swap" && invoice.swap !== void 0
|
|
1724
|
+
);
|
|
1725
|
+
if (swapInvoice !== void 0) {
|
|
1726
|
+
this.startedSwapInvoice = overlaySwapRefundStaging(swapInvoice, this.startedSwapInvoice);
|
|
1727
|
+
}
|
|
1728
|
+
const displayKey = checkoutSnapshotDisplayKey(snapshot);
|
|
1729
|
+
if (this.lastSnapshotDisplayKey === displayKey) return;
|
|
1730
|
+
this.lastSnapshotDisplayKey = displayKey;
|
|
1731
|
+
this.render();
|
|
1732
|
+
}
|
|
1733
|
+
render() {
|
|
1734
|
+
this.captureRefundAddressDraft();
|
|
1735
|
+
const invoiceAttr = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice);
|
|
1736
|
+
const deferredReady = this.isCreateMode() && this.latestCheckoutSnapshot !== void 0 && (invoiceAttr === null || invoiceAttr === "");
|
|
1737
|
+
if ((invoiceAttr === null || invoiceAttr === "") && !deferredReady) {
|
|
1738
|
+
if (this.isCreateMode()) {
|
|
1739
|
+
const { root: root2, inlineStyles: inlineStyles2 } = this.prepareShadowRoot();
|
|
1740
|
+
if (this.session.createError !== void 0) {
|
|
1741
|
+
root2.innerHTML = renderCheckoutCreateErrorHtml(this.session.createError, {
|
|
1742
|
+
theme: this.resolveTheme(),
|
|
1743
|
+
inlineStyles: inlineStyles2
|
|
1744
|
+
});
|
|
1745
|
+
root2.querySelector('[part="retry"]')?.addEventListener("click", () => this.session.retryCreateCheckout());
|
|
1746
|
+
return;
|
|
1747
|
+
}
|
|
1748
|
+
root2.innerHTML = renderCheckoutCreatingHtml({
|
|
1749
|
+
theme: this.resolveTheme(),
|
|
1750
|
+
inlineStyles: inlineStyles2
|
|
1751
|
+
});
|
|
1752
|
+
return;
|
|
1753
|
+
}
|
|
1754
|
+
this.replaceChildren();
|
|
1755
|
+
return;
|
|
1756
|
+
}
|
|
1757
|
+
const invoiceId = parseElementInvoiceId(
|
|
1758
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoiceId)
|
|
1759
|
+
);
|
|
1760
|
+
const invoice = invoiceAttr ?? "";
|
|
1761
|
+
const lightningRequested = !this.isCreateMode() || this.session.lightningRequested || invoice.length > 0;
|
|
1762
|
+
const decodeLinkUrl = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.decodeLinkUrl) ?? void 0;
|
|
1763
|
+
const selectedQuote = this.selectedSwapAsset === null ? void 0 : this.session.swapQuotes[this.selectedSwapAsset];
|
|
1764
|
+
const unavailableSwapQuote = selectedQuote?.available === false ? selectedQuote : void 0;
|
|
1765
|
+
const { root, inlineStyles } = this.prepareShadowRoot();
|
|
1766
|
+
root.innerHTML = renderCheckoutHtml({
|
|
1767
|
+
inlineStyles,
|
|
1768
|
+
...decodeLinkUrl === void 0 ? {} : { decodeLinkUrl },
|
|
1769
|
+
invoice_id: invoiceId,
|
|
1770
|
+
invoice,
|
|
1771
|
+
rail: parseElementRail(this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.rail)),
|
|
1772
|
+
payment_hash: this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentHash) ?? void 0,
|
|
1773
|
+
amount_msats: readElementAmountMsats(this),
|
|
1774
|
+
fiat_quote: readElementFiatQuote(this),
|
|
1775
|
+
status: parseElementStatus(
|
|
1776
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.status)
|
|
1777
|
+
),
|
|
1778
|
+
expires_at: readElementExpiresAt(this),
|
|
1779
|
+
theme: this.resolveTheme(),
|
|
1780
|
+
payment_wizard: parseBooleanAttribute(
|
|
1781
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentWizard)
|
|
1782
|
+
),
|
|
1783
|
+
lightningRequested: this.session.mintingLightning ? false : lightningRequested,
|
|
1784
|
+
wizard: {
|
|
1785
|
+
selectedMethod: this.selection.selectedMethod,
|
|
1786
|
+
selectedBitcoinRoute: this.selection.selectedBitcoinRoute,
|
|
1787
|
+
swapOptions: this.swapOptions,
|
|
1788
|
+
currenciesLoading: !this.swapOptionsLoaded,
|
|
1789
|
+
selectedSwapNetworks: this.selectedSwapNetworks,
|
|
1790
|
+
selectedPickerKey: this.selectedPickerKey,
|
|
1791
|
+
startingSwapAsset: this.session.startingSwapAsset,
|
|
1792
|
+
selectedSwapAsset: this.selectedSwapAsset,
|
|
1793
|
+
...this.latestCheckoutSnapshot?.amount_msats === void 0 ? {} : { amountMsats: this.latestCheckoutSnapshot.amount_msats },
|
|
1794
|
+
...this.latestCheckoutSnapshot?.fiat === void 0 ? {} : { fiat: this.latestCheckoutSnapshot.fiat },
|
|
1795
|
+
...this.latestCheckoutSnapshot?.reference === void 0 ? {} : { reference: this.latestCheckoutSnapshot.reference },
|
|
1796
|
+
...this.latestCheckoutSnapshot?.checkout_id === void 0 ? {} : { checkoutId: this.latestCheckoutSnapshot.checkout_id },
|
|
1797
|
+
lightningInvoice: invoice,
|
|
1798
|
+
...decodeLinkUrl === void 0 ? {} : { decodeLinkUrl },
|
|
1799
|
+
...this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentHash) === null ? {} : {
|
|
1800
|
+
paymentHash: this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentHash) ?? void 0
|
|
1801
|
+
},
|
|
1802
|
+
swapInvoice: this.currentSwapInvoice(),
|
|
1803
|
+
activeTutorialProviderId: this.activeTutorialProviderId,
|
|
1804
|
+
activeTutorialIndex: this.activeTutorialIndex,
|
|
1805
|
+
activeTutorialCopied: this.activeTutorialCopied,
|
|
1806
|
+
...this.session.swapStartError === void 0 ? {} : { swapStartError: this.session.swapStartError },
|
|
1807
|
+
...unavailableSwapQuote === void 0 ? {} : { unavailableSwapQuote },
|
|
1808
|
+
...this.latestCheckoutSnapshot === void 0 ? {} : { swapLimitContext: this.latestCheckoutSnapshot },
|
|
1809
|
+
...this.session.wizardError === void 0 ? {} : { wizardError: this.session.wizardError }
|
|
1810
|
+
},
|
|
1811
|
+
...this.lastCheckoutState === void 0 ? {} : { liveState: this.lastCheckoutState }
|
|
1812
|
+
});
|
|
1813
|
+
const copyButton = root.querySelector(OPENRECEIVE_CHECKOUT_ELEMENT_PART_SELECTORS.copy);
|
|
1814
|
+
if (invoice.length > 0) {
|
|
1815
|
+
copyButton?.addEventListener("click", () => {
|
|
1816
|
+
void (this.controller?.copyInvoice() ?? copyInvoice({ invoice, logger: options.logger })).then(() => {
|
|
1817
|
+
showElementCopyFeedback(copyButton);
|
|
1818
|
+
this.dispatchEvent(
|
|
1819
|
+
createCheckoutActionEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.copy)
|
|
1820
|
+
);
|
|
1821
|
+
}).catch((error) => this.dispatchError(error));
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
root.querySelector(OPENRECEIVE_CHECKOUT_ELEMENT_PART_SELECTORS.startOver)?.addEventListener("click", () => {
|
|
1825
|
+
this.dispatchEvent(
|
|
1826
|
+
createCheckoutActionEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.startOver)
|
|
1827
|
+
);
|
|
1828
|
+
});
|
|
1829
|
+
const qrTarget = root.querySelector(OPENRECEIVE_CHECKOUT_DATA_SELECTORS.qr);
|
|
1830
|
+
if (qrTarget !== null && invoice.length > 0) {
|
|
1831
|
+
void createQrSvg(invoice, {
|
|
1832
|
+
encoder: options.qrEncoder,
|
|
1833
|
+
width: 256
|
|
1834
|
+
}).then((svg) => {
|
|
1835
|
+
if (this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice) !== invoice) {
|
|
1836
|
+
return;
|
|
1837
|
+
}
|
|
1838
|
+
qrTarget.innerHTML = svg;
|
|
1839
|
+
}).catch((error) => this.dispatchError(error));
|
|
1840
|
+
}
|
|
1841
|
+
this.bindWizard(root, invoice, options.logger);
|
|
1842
|
+
this.renderSwapQrCodes(root);
|
|
1843
|
+
}
|
|
1844
|
+
/**
|
|
1845
|
+
* Shadow root plus the shared compiled stylesheet. `inlineStyles` is true only
|
|
1846
|
+
* where constructable stylesheets are unavailable and the markup must carry a
|
|
1847
|
+
* `<style>` of its own.
|
|
1848
|
+
*/
|
|
1849
|
+
prepareShadowRoot() {
|
|
1850
|
+
const root = this.shadowRoot ?? this.attachShadow({ mode: "open" });
|
|
1851
|
+
return { root, inlineStyles: !adoptCheckoutStyles(root) };
|
|
1852
|
+
}
|
|
1853
|
+
captureRefundAddressDraft() {
|
|
1854
|
+
const root = this.shadowRoot;
|
|
1855
|
+
if (root === null) return;
|
|
1856
|
+
const input = root.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapRefundAddress);
|
|
1857
|
+
if (!(input instanceof HTMLInputElement) || input.type === "hidden") return;
|
|
1858
|
+
this.refundAddressDraft = input.value;
|
|
1859
|
+
this.refundAddressDraftFocused = root.activeElement === input;
|
|
1860
|
+
this.refundAddressDraftSelectionStart = input.selectionStart;
|
|
1861
|
+
this.refundAddressDraftSelectionEnd = input.selectionEnd;
|
|
1862
|
+
}
|
|
1863
|
+
clearRefundAddressDraft() {
|
|
1864
|
+
this.refundAddressDraft = "";
|
|
1865
|
+
this.refundAddressDraftFocused = false;
|
|
1866
|
+
this.refundAddressDraftSelectionStart = null;
|
|
1867
|
+
this.refundAddressDraftSelectionEnd = null;
|
|
1868
|
+
}
|
|
1869
|
+
startCheckoutController() {
|
|
1870
|
+
const attributeSnapshot = this.currentCheckoutSnapshot();
|
|
1871
|
+
const latest = this.latestCheckoutSnapshot;
|
|
1872
|
+
const snapshot = latest?.active?.rail === "swap" ? latest : attributeSnapshot ?? latest;
|
|
1873
|
+
const prefix = this.resolvePollPrefix(snapshot?.reference);
|
|
1874
|
+
if (snapshot === void 0) {
|
|
1875
|
+
this.stopCheckoutController();
|
|
1876
|
+
return;
|
|
1877
|
+
}
|
|
1878
|
+
const polling = parseBooleanAttribute(
|
|
1879
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.polling)
|
|
1880
|
+
) !== false;
|
|
1881
|
+
const pollIntervalMs = parseOptionalInteger(
|
|
1882
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.pollIntervalMs),
|
|
1883
|
+
{ label: "poll-interval-ms" }
|
|
1884
|
+
);
|
|
1885
|
+
this.stopCheckoutController();
|
|
1886
|
+
this.controller = createCheckoutController({
|
|
1887
|
+
snapshot,
|
|
1888
|
+
...prefix === void 0 || !polling ? {} : { prefix },
|
|
1889
|
+
...pollIntervalMs === void 0 ? {} : { pollIntervalMs },
|
|
1890
|
+
logger: options.logger,
|
|
1891
|
+
onError: (error) => this.dispatchError(error),
|
|
1892
|
+
onState: (nextState) => this.applyCheckoutState(nextState),
|
|
1893
|
+
onSnapshot: (snapshot2) => this.handleControllerSnapshot(snapshot2)
|
|
1894
|
+
});
|
|
1895
|
+
this.controller.start();
|
|
1896
|
+
void this.controller.reloadState().catch((error) => this.dispatchError(error));
|
|
1897
|
+
}
|
|
1898
|
+
stopCheckoutController() {
|
|
1899
|
+
this.controller?.stop();
|
|
1900
|
+
this.controller = void 0;
|
|
1901
|
+
}
|
|
1902
|
+
/**
|
|
1903
|
+
* The mount every server route is derived from, matching React snapshot
|
|
1904
|
+
* mode: the `prefix` attribute, default `/openreceive`. Answers `undefined`
|
|
1905
|
+
* when there is no order to act on — nothing to poll, nothing to swap.
|
|
1906
|
+
*/
|
|
1907
|
+
resolvePollPrefix(reference) {
|
|
1908
|
+
const id = reference ?? this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference);
|
|
1909
|
+
if (id === null || id === void 0 || id.length === 0) return void 0;
|
|
1910
|
+
return this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.prefix) ?? OPENRECEIVE_DEFAULT_PREFIX2;
|
|
1911
|
+
}
|
|
1912
|
+
currentCheckoutSnapshot() {
|
|
1913
|
+
const reference = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference);
|
|
1914
|
+
const invoiceId = parseElementInvoiceId(
|
|
1915
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoiceId)
|
|
1916
|
+
);
|
|
1917
|
+
const invoice = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.invoice);
|
|
1918
|
+
if (invoiceId === void 0 || invoice === null) return void 0;
|
|
1919
|
+
const amountMsats = readElementAmountMsats(this);
|
|
1920
|
+
const expiresAt = readElementExpiresAt(this);
|
|
1921
|
+
const fiatQuote = readElementFiatQuote(this);
|
|
1922
|
+
const paymentHash = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.paymentHash);
|
|
1923
|
+
return createCheckoutSnapshotFromInvoice2(
|
|
1924
|
+
{
|
|
1925
|
+
invoice_id: invoiceId,
|
|
1926
|
+
invoice,
|
|
1927
|
+
rail: parseElementRail(this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.rail)),
|
|
1928
|
+
...paymentHash === null ? {} : { payment_hash: paymentHash },
|
|
1929
|
+
...amountMsats === void 0 ? {} : { amount_msats: amountMsats },
|
|
1930
|
+
...fiatQuote === void 0 ? {} : { fiat_quote: fiatQuote },
|
|
1931
|
+
transaction_state: transactionStateFromStatus(
|
|
1932
|
+
parseElementStatus(this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.status)) ?? "pending"
|
|
1933
|
+
),
|
|
1934
|
+
...expiresAt === void 0 ? {} : { expires_at: expiresAt }
|
|
1935
|
+
},
|
|
1936
|
+
{ ...reference === null ? {} : { reference } }
|
|
1937
|
+
);
|
|
1938
|
+
}
|
|
1939
|
+
applyCheckoutState(state) {
|
|
1940
|
+
const previous = this.lastCheckoutState;
|
|
1941
|
+
this.lastCheckoutState = state;
|
|
1942
|
+
this.session.writeOwnAttributes(() => {
|
|
1943
|
+
this.setAttributeIfChanged(
|
|
1944
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.status,
|
|
1945
|
+
deriveStatus2(state)
|
|
1946
|
+
);
|
|
1947
|
+
if (state.expires_at !== void 0) {
|
|
1948
|
+
this.setAttributeIfChanged(
|
|
1949
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.expiresAt,
|
|
1950
|
+
String(state.expires_at)
|
|
1951
|
+
);
|
|
1952
|
+
}
|
|
1953
|
+
});
|
|
1954
|
+
if (previous !== void 0 && this.isCountdownOnlyTick(previous, state)) {
|
|
1955
|
+
this.updateCountdownText(state);
|
|
1956
|
+
} else {
|
|
1957
|
+
this.render();
|
|
1958
|
+
}
|
|
1959
|
+
this.dispatchEvent(
|
|
1960
|
+
createCheckoutStateEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.state, state)
|
|
1961
|
+
);
|
|
1962
|
+
if (state.settled && state.reference !== this.announcedSettledReference) {
|
|
1963
|
+
this.announcedSettledReference = state.reference;
|
|
1964
|
+
this.dispatchEvent(
|
|
1965
|
+
createCheckoutStateEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.settled, state)
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
isCountdownOnlyTick(previous, next) {
|
|
1970
|
+
return previous.checkout_id === next.checkout_id && previous.invoice_id === next.invoice_id && previous.invoice === next.invoice && previous.rail === next.rail && previous.payment_hash === next.payment_hash && previous.transaction_state === next.transaction_state && previous.workflow_state === next.workflow_state && previous.phase === next.phase && previous.settled === next.settled && previous.terminal === next.terminal && previous.paid === next.paid && previous.expires_at === next.expires_at && previous.swap?.provider_state === next.swap?.provider_state;
|
|
1971
|
+
}
|
|
1972
|
+
/** Patch the countdown text nodes without rebuilding the shadow DOM. */
|
|
1973
|
+
updateCountdownText(state) {
|
|
1974
|
+
const root = this.shadowRoot;
|
|
1975
|
+
if (root === null) return;
|
|
1976
|
+
const model = createCheckoutStatusModel2(state);
|
|
1977
|
+
const countdown = root.querySelector('[part="countdown"] strong');
|
|
1978
|
+
if (countdown !== null && model.countdownLabel !== void 0) {
|
|
1979
|
+
countdown.textContent = model.countdownLabel;
|
|
1980
|
+
}
|
|
1981
|
+
const swapInvoice = this.currentSwapInvoice();
|
|
1982
|
+
if (swapInvoice !== void 0) {
|
|
1983
|
+
const display = createSwapDisplayModel2(swapInvoice, {});
|
|
1984
|
+
const swapCountdown = root.querySelector('[part="swap-countdown"]');
|
|
1985
|
+
if (swapCountdown !== null && display !== void 0) {
|
|
1986
|
+
swapCountdown.textContent = display.countdownLabel;
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
}
|
|
1990
|
+
setAttributeIfChanged(name, value) {
|
|
1991
|
+
if (this.getAttribute(name) !== value) this.setAttribute(name, value);
|
|
1992
|
+
}
|
|
1993
|
+
bindWizard(root, invoice, logger) {
|
|
1994
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.method).forEach((button) => {
|
|
1995
|
+
button.addEventListener("click", () => {
|
|
1996
|
+
if (button.hasAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.pickerContinue)) return;
|
|
1997
|
+
if (button.hasAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.pickerSelect)) return;
|
|
1998
|
+
const method = parsePaymentMethod(
|
|
1999
|
+
button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.method)
|
|
2000
|
+
);
|
|
2001
|
+
if (method === null) return;
|
|
2002
|
+
this.selectedPickerKey = null;
|
|
2003
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2004
|
+
type: "select_method",
|
|
2005
|
+
method
|
|
2006
|
+
});
|
|
2007
|
+
if (method === "bitcoin") {
|
|
2008
|
+
void this.session.ensureLightning();
|
|
2009
|
+
}
|
|
2010
|
+
this.render();
|
|
2011
|
+
});
|
|
2012
|
+
});
|
|
2013
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.pickerSelect).forEach((button) => {
|
|
2014
|
+
button.addEventListener("click", () => {
|
|
2015
|
+
if (!(button instanceof HTMLButtonElement) || button.disabled) return;
|
|
2016
|
+
const key = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.pickerSelect);
|
|
2017
|
+
if (key === null || key.length === 0) return;
|
|
2018
|
+
const methodPick = parseMethodPickerKey(key);
|
|
2019
|
+
if (methodPick !== null) {
|
|
2020
|
+
const method = parsePaymentMethod(methodPick.methodId);
|
|
2021
|
+
if (method === null) return;
|
|
2022
|
+
this.selectedPickerKey = null;
|
|
2023
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2024
|
+
type: "select_method",
|
|
2025
|
+
method
|
|
2026
|
+
});
|
|
2027
|
+
if (method === "bitcoin") {
|
|
2028
|
+
void this.session.ensureLightning();
|
|
2029
|
+
}
|
|
2030
|
+
this.render();
|
|
2031
|
+
return;
|
|
2032
|
+
}
|
|
2033
|
+
const previousKey = this.selectedPickerKey;
|
|
2034
|
+
this.selectedPickerKey = key;
|
|
2035
|
+
const entries = buildMethodGridEntries2(paymentMethods2, this.swapOptions);
|
|
2036
|
+
const nextGroup = findSwapGridGroup2(entries, key);
|
|
2037
|
+
if (nextGroup !== void 0 && nextGroup.options.length === 1) {
|
|
2038
|
+
const option = nextGroup.options.find((entry) => entry.available !== false) ?? nextGroup.options[0];
|
|
2039
|
+
if (option === void 0 || option.available === false) return;
|
|
2040
|
+
this.selectedPickerKey = null;
|
|
2041
|
+
void this.session.startSwap(option.pay_in_asset);
|
|
2042
|
+
return;
|
|
2043
|
+
}
|
|
2044
|
+
this.selectedSwapNetworks = updateSelectedSwapNetworks({
|
|
2045
|
+
entries,
|
|
2046
|
+
nextKey: key,
|
|
2047
|
+
previousKey,
|
|
2048
|
+
selectedNetworks: this.selectedSwapNetworks
|
|
2049
|
+
});
|
|
2050
|
+
this.render();
|
|
2051
|
+
});
|
|
2052
|
+
});
|
|
2053
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.pickerContinue).forEach((button) => {
|
|
2054
|
+
button.addEventListener("click", () => {
|
|
2055
|
+
if (!(button instanceof HTMLButtonElement) || button.disabled) return;
|
|
2056
|
+
const method = parsePaymentMethod(
|
|
2057
|
+
button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.method)
|
|
2058
|
+
);
|
|
2059
|
+
if (method !== null) {
|
|
2060
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2061
|
+
type: "select_method",
|
|
2062
|
+
method
|
|
2063
|
+
});
|
|
2064
|
+
this.render();
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
const payInAsset = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapStart);
|
|
2068
|
+
if (payInAsset === null) return;
|
|
2069
|
+
markElementConfirmButtonBusy(button);
|
|
2070
|
+
void this.session.startSwap(payInAsset);
|
|
2071
|
+
});
|
|
2072
|
+
});
|
|
2073
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.breadcrumb).forEach((button) => {
|
|
2074
|
+
button.addEventListener("click", () => {
|
|
2075
|
+
const target = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.breadcrumb);
|
|
2076
|
+
if (target === "swap-asset") {
|
|
2077
|
+
this.selectedSwapAsset = null;
|
|
2078
|
+
this.selectedPickerKey = null;
|
|
2079
|
+
this.selectedSwapNetworks = {};
|
|
2080
|
+
this.render();
|
|
2081
|
+
return;
|
|
2082
|
+
}
|
|
2083
|
+
if (target === "method") {
|
|
2084
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2085
|
+
type: "change_method"
|
|
2086
|
+
});
|
|
2087
|
+
this.render();
|
|
2088
|
+
return;
|
|
2089
|
+
}
|
|
2090
|
+
if (target === "route") {
|
|
2091
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2092
|
+
type: "change_route"
|
|
2093
|
+
});
|
|
2094
|
+
this.render();
|
|
2095
|
+
}
|
|
2096
|
+
});
|
|
2097
|
+
});
|
|
2098
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.route).forEach((button) => {
|
|
2099
|
+
button.addEventListener("click", () => {
|
|
2100
|
+
const route = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.route);
|
|
2101
|
+
if (route === null) return;
|
|
2102
|
+
this.selection = updatePaymentWizardSelection(this.selection, {
|
|
2103
|
+
type: "select_route",
|
|
2104
|
+
route
|
|
2105
|
+
});
|
|
2106
|
+
this.render();
|
|
2107
|
+
});
|
|
2108
|
+
});
|
|
2109
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapStart).forEach((button) => {
|
|
2110
|
+
button.addEventListener("click", () => {
|
|
2111
|
+
if (button.hasAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.pickerContinue)) return;
|
|
2112
|
+
const payInAsset = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapStart);
|
|
2113
|
+
if (payInAsset === null) return;
|
|
2114
|
+
if (button instanceof HTMLButtonElement) markElementConfirmButtonBusy(button);
|
|
2115
|
+
void this.session.startSwap(payInAsset);
|
|
2116
|
+
});
|
|
2117
|
+
});
|
|
2118
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapNetworkValue).forEach((button) => {
|
|
2119
|
+
button.addEventListener("click", () => {
|
|
2120
|
+
if (!(button instanceof HTMLButtonElement) || button.disabled) return;
|
|
2121
|
+
const groupKey = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapNetwork);
|
|
2122
|
+
const payInAsset = button.getAttribute(
|
|
2123
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapNetworkValue
|
|
2124
|
+
);
|
|
2125
|
+
if (groupKey === null || groupKey.length === 0 || payInAsset === null) return;
|
|
2126
|
+
const details = button.closest("details");
|
|
2127
|
+
if (details instanceof HTMLDetailsElement) details.open = false;
|
|
2128
|
+
this.selectedSwapNetworks = {
|
|
2129
|
+
...this.selectedSwapNetworks,
|
|
2130
|
+
[groupKey]: payInAsset
|
|
2131
|
+
};
|
|
2132
|
+
this.render();
|
|
2133
|
+
});
|
|
2134
|
+
});
|
|
2135
|
+
root.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapBack)?.addEventListener("click", () => {
|
|
2136
|
+
const current = this.currentSwapInvoice();
|
|
2137
|
+
this.dismissedSwapInvoiceId = current?.invoice_id ?? null;
|
|
2138
|
+
this.selectedSwapAsset = null;
|
|
2139
|
+
this.selectedPickerKey = null;
|
|
2140
|
+
this.selectedSwapNetworks = {};
|
|
2141
|
+
this.clearRefundAddressDraft();
|
|
2142
|
+
void this.session.ensureLightning();
|
|
2143
|
+
this.render();
|
|
2144
|
+
});
|
|
2145
|
+
wireSwapSelectAllInputs(root);
|
|
2146
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapCopy).forEach((button) => {
|
|
2147
|
+
button.addEventListener("click", () => {
|
|
2148
|
+
const value = button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapCopy);
|
|
2149
|
+
if (value === null) return;
|
|
2150
|
+
void globalThis.navigator?.clipboard?.writeText(value).then(() => showElementCopyFeedback(button)).catch((error) => this.dispatchError(error));
|
|
2151
|
+
});
|
|
2152
|
+
});
|
|
2153
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapRefundForm).forEach((form) => {
|
|
2154
|
+
const input = form.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapRefundAddress);
|
|
2155
|
+
const errorEl = form.querySelector(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapRefundError);
|
|
2156
|
+
const payInAsset = form.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapRefundPayInAsset) ?? "";
|
|
2157
|
+
const networkLabel = form.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapRefundNetworkLabel) ?? "refund";
|
|
2158
|
+
const setRefundAddressError = (message) => {
|
|
2159
|
+
if (input instanceof HTMLInputElement) {
|
|
2160
|
+
input.className = message === void 0 ? orClasses7.swapRefundInput : orClasses7.swapRefundInputInvalid;
|
|
2161
|
+
if (message === void 0) input.removeAttribute("aria-invalid");
|
|
2162
|
+
else input.setAttribute("aria-invalid", "true");
|
|
2163
|
+
}
|
|
2164
|
+
if (errorEl instanceof HTMLElement) {
|
|
2165
|
+
if (message === void 0) {
|
|
2166
|
+
errorEl.textContent = "";
|
|
2167
|
+
errorEl.hidden = true;
|
|
2168
|
+
} else {
|
|
2169
|
+
errorEl.textContent = message;
|
|
2170
|
+
errorEl.hidden = false;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
const validateRefundAddress = (address, showEmpty) => {
|
|
2175
|
+
if (address.length === 0 && !showEmpty) return void 0;
|
|
2176
|
+
return getSwapRefundFormError(payInAsset, address, networkLabel);
|
|
2177
|
+
};
|
|
2178
|
+
if (input instanceof HTMLInputElement && input.type !== "hidden") {
|
|
2179
|
+
if (this.refundAddressDraft.length > 0) {
|
|
2180
|
+
input.value = this.refundAddressDraft;
|
|
2181
|
+
}
|
|
2182
|
+
input.addEventListener("input", () => {
|
|
2183
|
+
this.refundAddressDraft = input.value;
|
|
2184
|
+
const address = input.value.trim();
|
|
2185
|
+
if (address.length === 0) {
|
|
2186
|
+
setRefundAddressError(void 0);
|
|
2187
|
+
return;
|
|
2188
|
+
}
|
|
2189
|
+
setRefundAddressError(validateRefundAddress(address, false));
|
|
2190
|
+
});
|
|
2191
|
+
input.addEventListener("blur", () => {
|
|
2192
|
+
const address = input.value.trim();
|
|
2193
|
+
if (address.length === 0) return;
|
|
2194
|
+
setRefundAddressError(validateRefundAddress(address, false));
|
|
2195
|
+
});
|
|
2196
|
+
if (this.refundAddressDraftFocused) {
|
|
2197
|
+
input.focus();
|
|
2198
|
+
const start = this.refundAddressDraftSelectionStart ?? input.value.length;
|
|
2199
|
+
const end = this.refundAddressDraftSelectionEnd ?? input.value.length;
|
|
2200
|
+
input.setSelectionRange(start, end);
|
|
2201
|
+
}
|
|
2202
|
+
const restoredError = validateRefundAddress(input.value.trim(), false);
|
|
2203
|
+
if (restoredError !== void 0) setRefundAddressError(restoredError);
|
|
2204
|
+
}
|
|
2205
|
+
form.addEventListener("submit", (event) => {
|
|
2206
|
+
event.preventDefault();
|
|
2207
|
+
const attemptId = form.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapRefundForm);
|
|
2208
|
+
const refundAllowed = form.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapRefundAllowed) === "true";
|
|
2209
|
+
const confirm = form.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapRefundConfirm) === "true";
|
|
2210
|
+
const refundAddress = input instanceof HTMLInputElement ? input.value.trim() : "";
|
|
2211
|
+
if (attemptId === null || !refundAllowed) return;
|
|
2212
|
+
const error = validateRefundAddress(refundAddress, true);
|
|
2213
|
+
if (error !== void 0) {
|
|
2214
|
+
setRefundAddressError(error);
|
|
2215
|
+
return;
|
|
2216
|
+
}
|
|
2217
|
+
setRefundAddressError(void 0);
|
|
2218
|
+
void this.refundSwap(attemptId, refundAddress, confirm);
|
|
2219
|
+
});
|
|
2220
|
+
});
|
|
2221
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.providerTutorial).forEach((button) => {
|
|
2222
|
+
button.addEventListener("click", () => {
|
|
2223
|
+
const providerId = button.getAttribute(
|
|
2224
|
+
OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.providerTutorial
|
|
2225
|
+
);
|
|
2226
|
+
if (providerId === null) return;
|
|
2227
|
+
if (providerId === "") {
|
|
2228
|
+
this.activeTutorialProviderId = null;
|
|
2229
|
+
this.activeTutorialIndex = 0;
|
|
2230
|
+
this.activeTutorialCopied = false;
|
|
2231
|
+
this.render();
|
|
2232
|
+
return;
|
|
2233
|
+
}
|
|
2234
|
+
const index = Number(
|
|
2235
|
+
button.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.providerTutorialIndex) ?? "0"
|
|
2236
|
+
);
|
|
2237
|
+
if (this.activeTutorialProviderId !== providerId) {
|
|
2238
|
+
this.activeTutorialCopied = false;
|
|
2239
|
+
}
|
|
2240
|
+
this.activeTutorialProviderId = providerId;
|
|
2241
|
+
this.activeTutorialIndex = Number.isSafeInteger(index) && index >= 0 ? index : 0;
|
|
2242
|
+
this.render();
|
|
2243
|
+
});
|
|
2244
|
+
});
|
|
2245
|
+
root.querySelector('[part="tutorial-copy"]')?.addEventListener("click", () => {
|
|
2246
|
+
void copyInvoice({ invoice, logger }).then(() => {
|
|
2247
|
+
this.activeTutorialCopied = true;
|
|
2248
|
+
if (this.activeTutorialProviderId !== null) {
|
|
2249
|
+
this.dispatchEvent(createCheckoutProviderCopyEvent(this.activeTutorialProviderId));
|
|
2250
|
+
}
|
|
2251
|
+
this.dispatchEvent(createCheckoutActionEvent(OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS.copy));
|
|
2252
|
+
this.render();
|
|
2253
|
+
}).catch((error) => this.dispatchError(error));
|
|
2254
|
+
});
|
|
2255
|
+
const tutorial = root.querySelector('[part="tutorial"]');
|
|
2256
|
+
tutorial?.addEventListener("click", (event) => {
|
|
2257
|
+
if (event.target !== event.currentTarget) return;
|
|
2258
|
+
this.activeTutorialProviderId = null;
|
|
2259
|
+
this.activeTutorialIndex = 0;
|
|
2260
|
+
this.activeTutorialCopied = false;
|
|
2261
|
+
this.render();
|
|
2262
|
+
});
|
|
2263
|
+
tutorial?.addEventListener("keydown", (event) => {
|
|
2264
|
+
if (!(event instanceof KeyboardEvent)) return;
|
|
2265
|
+
if (event.key !== "Escape" || this.activeTutorialProviderId === null) return;
|
|
2266
|
+
this.activeTutorialProviderId = null;
|
|
2267
|
+
this.activeTutorialIndex = 0;
|
|
2268
|
+
this.activeTutorialCopied = false;
|
|
2269
|
+
this.render();
|
|
2270
|
+
});
|
|
2271
|
+
if (tutorial instanceof HTMLElement && this.activeTutorialProviderId !== null && this.focusedTutorialProviderId !== this.activeTutorialProviderId) {
|
|
2272
|
+
this.focusedTutorialProviderId = this.activeTutorialProviderId;
|
|
2273
|
+
tutorial.focus();
|
|
2274
|
+
}
|
|
2275
|
+
if (this.activeTutorialProviderId === null) {
|
|
2276
|
+
this.focusedTutorialProviderId = null;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
async refundSwap(attemptId, refundAddress, confirm) {
|
|
2280
|
+
const reference = this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.reference);
|
|
2281
|
+
const prefix = this.resolvePollPrefix(reference ?? void 0);
|
|
2282
|
+
if (prefix === void 0) return;
|
|
2283
|
+
try {
|
|
2284
|
+
this.startedSwapInvoice = await requestSwapRefund({
|
|
2285
|
+
fetch: globalThis.fetch,
|
|
2286
|
+
prefix,
|
|
2287
|
+
...reference === null ? {} : { reference },
|
|
2288
|
+
invoices: [this.startedSwapInvoice, ...this.latestCheckoutSnapshot?.invoices ?? []],
|
|
2289
|
+
attemptId,
|
|
2290
|
+
refundAddress,
|
|
2291
|
+
confirm,
|
|
2292
|
+
logger: options.logger
|
|
2293
|
+
});
|
|
2294
|
+
this.dismissedSwapInvoiceId = null;
|
|
2295
|
+
this.render();
|
|
2296
|
+
} catch (error) {
|
|
2297
|
+
this.dispatchError(error);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2300
|
+
currentSwapInvoice() {
|
|
2301
|
+
const fromSnapshot = this.latestCheckoutSnapshot?.invoices.find(
|
|
2302
|
+
(invoice) => invoice.rail === "swap" && invoice.swap !== void 0 && invoice.invoice_id !== this.dismissedSwapInvoiceId
|
|
2303
|
+
);
|
|
2304
|
+
if (this.startedSwapInvoice === void 0 || this.startedSwapInvoice.invoice_id === this.dismissedSwapInvoiceId) {
|
|
2305
|
+
return fromSnapshot;
|
|
2306
|
+
}
|
|
2307
|
+
const matched = this.latestCheckoutSnapshot?.invoices.find(
|
|
2308
|
+
(invoice) => invoice.invoice_id === this.startedSwapInvoice?.invoice_id
|
|
2309
|
+
) ?? this.startedSwapInvoice;
|
|
2310
|
+
return overlaySwapRefundStaging(matched, this.startedSwapInvoice);
|
|
2311
|
+
}
|
|
2312
|
+
renderSwapQrCodes(root) {
|
|
2313
|
+
root.querySelectorAll(OPENRECEIVE_PAYMENT_WIZARD_SELECTORS3.swapQr).forEach((target) => {
|
|
2314
|
+
const payload = target.getAttribute(OPENRECEIVE_PAYMENT_WIZARD_ATTRIBUTES7.swapQr);
|
|
2315
|
+
if (payload === null) return;
|
|
2316
|
+
void createQrPayloadSvg(payload, {
|
|
2317
|
+
encoder: options.qrEncoder,
|
|
2318
|
+
width: 220
|
|
2319
|
+
}).then((svg) => {
|
|
2320
|
+
target.innerHTML = svg;
|
|
2321
|
+
}).catch((error) => this.dispatchError(error));
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
currentThemeOption() {
|
|
2325
|
+
const theme = parseResolvedTheme(
|
|
2326
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme)
|
|
2327
|
+
);
|
|
2328
|
+
return theme === void 0 ? {} : { theme };
|
|
2329
|
+
}
|
|
2330
|
+
/** Explicit `theme` attr wins; otherwise inherit nearest ancestor `[data-theme]`. */
|
|
2331
|
+
resolveTheme() {
|
|
2332
|
+
const attr = parseResolvedTheme(
|
|
2333
|
+
this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme)
|
|
2334
|
+
);
|
|
2335
|
+
if (attr !== void 0) return attr;
|
|
2336
|
+
const ancestor = this.closest("[data-theme]");
|
|
2337
|
+
const inherited = parseResolvedTheme(ancestor?.getAttribute("data-theme"));
|
|
2338
|
+
if (inherited !== void 0) return inherited;
|
|
2339
|
+
return "light";
|
|
2340
|
+
}
|
|
2341
|
+
syncThemeAncestorObserver() {
|
|
2342
|
+
const hasOwnTheme = parseResolvedTheme(this.getAttribute(OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES3.theme)) !== void 0;
|
|
2343
|
+
if (hasOwnTheme) {
|
|
2344
|
+
this.stopThemeAncestorObserver();
|
|
2345
|
+
return;
|
|
2346
|
+
}
|
|
2347
|
+
const ancestor = this.closest("[data-theme]");
|
|
2348
|
+
if (ancestor === this.observedThemeAncestor && this.themeAncestorObserver !== void 0) {
|
|
2349
|
+
return;
|
|
2350
|
+
}
|
|
2351
|
+
this.stopThemeAncestorObserver();
|
|
2352
|
+
if (ancestor === null) return;
|
|
2353
|
+
const MutationObserverCtor = globalThis.MutationObserver;
|
|
2354
|
+
if (MutationObserverCtor === void 0) return;
|
|
2355
|
+
this.observedThemeAncestor = ancestor;
|
|
2356
|
+
this.themeAncestorObserver = new MutationObserverCtor(() => {
|
|
2357
|
+
this.render();
|
|
2358
|
+
this.syncThemeAncestorObserver();
|
|
2359
|
+
});
|
|
2360
|
+
this.themeAncestorObserver.observe(ancestor, {
|
|
2361
|
+
attributes: true,
|
|
2362
|
+
attributeFilter: ["data-theme"]
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
stopThemeAncestorObserver() {
|
|
2366
|
+
this.themeAncestorObserver?.disconnect();
|
|
2367
|
+
this.themeAncestorObserver = void 0;
|
|
2368
|
+
this.observedThemeAncestor = null;
|
|
2369
|
+
}
|
|
2370
|
+
dispatchError(error) {
|
|
2371
|
+
this.dispatchEvent(createCheckoutErrorEvent(error));
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
class ThemeToggleElement extends HTMLElementCtor {
|
|
2375
|
+
observer;
|
|
2376
|
+
observedTarget = null;
|
|
2377
|
+
static get observedAttributes() {
|
|
2378
|
+
return [
|
|
2379
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.rootSelector,
|
|
2380
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.checkoutSelector,
|
|
2381
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.defaultTheme,
|
|
2382
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.storageKey
|
|
2383
|
+
];
|
|
2384
|
+
}
|
|
2385
|
+
connectedCallback() {
|
|
2386
|
+
this.render();
|
|
2387
|
+
this.startObserver();
|
|
2388
|
+
}
|
|
2389
|
+
attributeChangedCallback() {
|
|
2390
|
+
if (!this.isConnected) return;
|
|
2391
|
+
this.render();
|
|
2392
|
+
this.startObserver();
|
|
2393
|
+
}
|
|
2394
|
+
disconnectedCallback() {
|
|
2395
|
+
this.stopObserver();
|
|
2396
|
+
}
|
|
2397
|
+
render() {
|
|
2398
|
+
const root = this.shadowRoot ?? this.attachShadow({ mode: "open" });
|
|
2399
|
+
const inlineStyles = !adoptCheckoutStyles(root);
|
|
2400
|
+
const theme = this.syncTheme();
|
|
2401
|
+
root.innerHTML = renderThemeToggleHtml(theme.toggleLabel, { inlineStyles });
|
|
2402
|
+
root.querySelector(OPENRECEIVE_THEME_TOGGLE_ELEMENT_PART_SELECTORS.button)?.addEventListener("click", () => {
|
|
2403
|
+
const nextTheme = toggleStoredThemeControls(this.themeTargets(), this.themeOptions());
|
|
2404
|
+
this.dispatchEvent(createThemeChangeEvent(nextTheme));
|
|
2405
|
+
this.render();
|
|
2406
|
+
});
|
|
2407
|
+
}
|
|
2408
|
+
syncTheme() {
|
|
2409
|
+
return syncStoredThemeControls(this.themeTargets(), this.themeOptions());
|
|
2410
|
+
}
|
|
2411
|
+
/**
|
|
2412
|
+
* Watch only what can invalidate the toggle: the theme attributes it mirrors,
|
|
2413
|
+
* and children arriving under the same wrapper (a checkout element mounted
|
|
2414
|
+
* after the toggle). Observing the whole page body with `subtree` ran a
|
|
2415
|
+
* querySelector sweep on every unrelated DOM mutation on the host page.
|
|
2416
|
+
*/
|
|
2417
|
+
startObserver() {
|
|
2418
|
+
const MutationObserverCtor = globalThis.MutationObserver;
|
|
2419
|
+
if (MutationObserverCtor === void 0) return;
|
|
2420
|
+
const target = this.themeTargets().root ?? this.ownerDocument.documentElement;
|
|
2421
|
+
if (target === null || target === void 0) return;
|
|
2422
|
+
if (this.observer !== void 0 && this.observedTarget === target) return;
|
|
2423
|
+
this.stopObserver();
|
|
2424
|
+
this.observedTarget = target;
|
|
2425
|
+
this.observer = new MutationObserverCtor(() => {
|
|
2426
|
+
this.syncTheme();
|
|
2427
|
+
});
|
|
2428
|
+
this.observer.observe(target, {
|
|
2429
|
+
attributes: true,
|
|
2430
|
+
attributeFilter: ["data-theme", "data-openreceive-theme"],
|
|
2431
|
+
childList: true
|
|
2432
|
+
});
|
|
2433
|
+
}
|
|
2434
|
+
stopObserver() {
|
|
2435
|
+
this.observer?.disconnect();
|
|
2436
|
+
this.observer = void 0;
|
|
2437
|
+
this.observedTarget = null;
|
|
2438
|
+
}
|
|
2439
|
+
themeTargets() {
|
|
2440
|
+
const rootSelector = this.getAttribute(
|
|
2441
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.rootSelector
|
|
2442
|
+
);
|
|
2443
|
+
const checkoutSelector = this.getAttribute(
|
|
2444
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.checkoutSelector
|
|
2445
|
+
);
|
|
2446
|
+
const button = this.shadowRoot?.querySelector(OPENRECEIVE_THEME_TOGGLE_ELEMENT_PART_SELECTORS.button) ?? null;
|
|
2447
|
+
const defaultRoot = this.closest("[data-openreceive-theme]");
|
|
2448
|
+
return {
|
|
2449
|
+
root: rootSelector === null ? defaultRoot ?? void 0 : this.ownerDocument.querySelector(rootSelector) ?? void 0,
|
|
2450
|
+
checkout: checkoutSelector === null ? defaultRoot?.querySelector(tagName) ?? void 0 : this.ownerDocument.querySelector(checkoutSelector) ?? void 0,
|
|
2451
|
+
toggle: button
|
|
2452
|
+
};
|
|
2453
|
+
}
|
|
2454
|
+
themeOptions() {
|
|
2455
|
+
const defaultTheme = parseThemePreference(
|
|
2456
|
+
this.getAttribute(OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.defaultTheme)
|
|
2457
|
+
);
|
|
2458
|
+
return {
|
|
2459
|
+
...defaultTheme === void 0 ? {} : { defaultTheme },
|
|
2460
|
+
...this.getAttribute(OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.storageKey) === null ? {} : {
|
|
2461
|
+
storageKey: this.getAttribute(OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES.storageKey) ?? void 0
|
|
2462
|
+
}
|
|
2463
|
+
};
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
if (registry.get(tagName) === void 0) {
|
|
2467
|
+
registry.define(tagName, CheckoutElement);
|
|
2468
|
+
}
|
|
2469
|
+
if (registry.get(themeToggleTagName) === void 0) {
|
|
2470
|
+
registry.define(themeToggleTagName, ThemeToggleElement);
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
|
|
2474
|
+
export {
|
|
2475
|
+
renderTransactionDetailsHtml,
|
|
2476
|
+
createTransactionDetailsElement,
|
|
2477
|
+
renderCheckoutHtml,
|
|
2478
|
+
renderCheckoutCreateErrorHtml,
|
|
2479
|
+
renderThemeToggleHtml,
|
|
2480
|
+
defineElements,
|
|
2481
|
+
createThemeToggleElement,
|
|
2482
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES4 as OPENRECEIVE_CHECKOUT_ELEMENT_ATTRIBUTES,
|
|
2483
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS2 as OPENRECEIVE_CHECKOUT_ELEMENT_EVENTS,
|
|
2484
|
+
OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME2 as OPENRECEIVE_CHECKOUT_ELEMENT_TAG_NAME,
|
|
2485
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES2 as OPENRECEIVE_THEME_TOGGLE_ELEMENT_ATTRIBUTES,
|
|
2486
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_EVENTS,
|
|
2487
|
+
OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME2 as OPENRECEIVE_THEME_TOGGLE_ELEMENT_TAG_NAME
|
|
2488
|
+
};
|