Refactor markup and styles to re-use existing code where possible
This commit is contained in:
@@ -30,6 +30,10 @@ export default class Modal extends Component {
|
|||||||
return config.environment === 'test';
|
return config.environment === 'test';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get shouldPortal() {
|
||||||
|
return !this.isTesting && !this.args.inline;
|
||||||
|
}
|
||||||
|
|
||||||
get destinationElement() {
|
get destinationElement() {
|
||||||
return document.getElementById('modal-portal') || document.body;
|
return document.getElementById('modal-portal') || document.body;
|
||||||
}
|
}
|
||||||
@@ -48,15 +52,7 @@ export default class Modal extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{#if this.isTesting}}
|
{{#if this.shouldPortal}}
|
||||||
<ModalContent
|
|
||||||
@close={{this.close}}
|
|
||||||
@stopProp={{this.stopProp}}
|
|
||||||
@disableClose={{@disableClose}}
|
|
||||||
>
|
|
||||||
{{yield}}
|
|
||||||
</ModalContent>
|
|
||||||
{{else}}
|
|
||||||
{{#in-element this.destinationElement}}
|
{{#in-element this.destinationElement}}
|
||||||
<ModalContent
|
<ModalContent
|
||||||
@close={{this.close}}
|
@close={{this.close}}
|
||||||
@@ -66,6 +62,14 @@ export default class Modal extends Component {
|
|||||||
{{yield}}
|
{{yield}}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
{{/in-element}}
|
{{/in-element}}
|
||||||
|
{{else}}
|
||||||
|
<ModalContent
|
||||||
|
@close={{this.close}}
|
||||||
|
@stopProp={{this.stopProp}}
|
||||||
|
@disableClose={{@disableClose}}
|
||||||
|
>
|
||||||
|
{{yield}}
|
||||||
|
</ModalContent>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ export default class PhotoGallery extends Component {
|
|||||||
e.target.closest('.carousel-nav-btn') ||
|
e.target.closest('.carousel-nav-btn') ||
|
||||||
e.target.closest('.close-btn') ||
|
e.target.closest('.close-btn') ||
|
||||||
e.target.closest('.photo-gallery-header') ||
|
e.target.closest('.photo-gallery-header') ||
|
||||||
e.target.closest('.zap-photo-modal-overlay')
|
e.target.closest('.modal-overlay')
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { task } from 'ember-concurrency';
|
|||||||
import { eq } from 'ember-truth-helpers';
|
import { eq } from 'ember-truth-helpers';
|
||||||
import qrCode from '../modifiers/qr-code';
|
import qrCode from '../modifiers/qr-code';
|
||||||
import Icon from './icon';
|
import Icon from './icon';
|
||||||
|
import Modal from './modal';
|
||||||
|
|
||||||
const SLIDER_MIN_SATS = 10;
|
const SLIDER_MIN_SATS = 10;
|
||||||
const SLIDER_MAX_SATS = 100_000;
|
const SLIDER_MAX_SATS = 100_000;
|
||||||
@@ -210,11 +211,6 @@ export default class ZapPhotoModal extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action
|
|
||||||
stopPropagation(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
_startReceiptSubscription(zapRequest) {
|
_startReceiptSubscription(zapRequest) {
|
||||||
this._cleanupReceiptSub();
|
this._cleanupReceiptSub();
|
||||||
this._zapRequest = zapRequest;
|
this._zapRequest = zapRequest;
|
||||||
@@ -291,69 +287,52 @@ export default class ZapPhotoModal extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
{{! template-lint-disable no-invalid-interactive }}
|
<Modal @inline={{true}} @onClose={{this.resetAndClose}}>
|
||||||
<div
|
|
||||||
class="zap-photo-modal-overlay"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
{{on "click" this.stopPropagation}}
|
|
||||||
>
|
|
||||||
<div class="zap-photo-modal">
|
<div class="zap-photo-modal">
|
||||||
<div class="zap-photo-modal-header">
|
<h2>Zap Photo</h2>
|
||||||
<h3>Zap Photo</h3>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="btn-text zap-modal-close"
|
|
||||||
{{on "click" this.resetAndClose}}
|
|
||||||
aria-label="Close"
|
|
||||||
>
|
|
||||||
<Icon @name="x" @size={{20}} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{! Lightning address status }}
|
{{! Lightning address status }}
|
||||||
<div class="zap-modal-status">
|
|
||||||
{{#if this.lnurlLoading}}
|
{{#if this.lnurlLoading}}
|
||||||
<div class="zap-modal-loading">Loading lightning address...</div>
|
<p class="meta-info">Loading lightning address...</p>
|
||||||
{{else if this.lightningAddress}}
|
{{else if this.lightningAddress}}
|
||||||
<div class="zap-modal-address">
|
<div class="lightning-address">
|
||||||
<span class="zap-modal-badge">Lightning</span>
|
<span class="lightning-badge">Lightning</span>
|
||||||
<span
|
<span
|
||||||
class="zap-modal-address-text"
|
class="lightning-address-text"
|
||||||
>{{this.lightningAddress}}</span>
|
>{{this.lightningAddress}}</span>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if this.lnurlError}}
|
{{#if this.lnurlError}}
|
||||||
<div class="zap-modal-error-msg">{{this.lnurlError}}</div>
|
<div class="alert alert-error">{{this.lnurlError}}</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
|
||||||
|
|
||||||
{{! Step: Select Amount }}
|
{{! Step: Select Amount }}
|
||||||
{{#if (eq this.step "select-amount")}}
|
{{#if (eq this.step "select-amount")}}
|
||||||
<div class="zap-modal-body">
|
<div class="amount-display">
|
||||||
<div class="zap-amount-display">
|
<span
|
||||||
<span class="zap-amount-value">{{this.formattedAmount}}</span>
|
class="amount-value zap-amount"
|
||||||
<span class="zap-amount-unit">sats</span>
|
>{{this.formattedAmount}}</span>
|
||||||
|
<span class="amount-unit">sats</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="range"
|
||||||
|
class="zap-slider"
|
||||||
min="0"
|
min="0"
|
||||||
max={{this.sliderSteps}}
|
max={{this.sliderSteps}}
|
||||||
value={{this.sliderPosition}}
|
value={{this.sliderPosition}}
|
||||||
aria-label="Zap amount in sats"
|
aria-label="Zap amount in sats"
|
||||||
{{on "input" this.updateSlider}}
|
{{on "input" this.updateSlider}}
|
||||||
class="zap-slider"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="zap-slider-ticks">
|
<div class="slider-ticks">
|
||||||
{{#each this.sliderTicks as |label|}}
|
{{#each this.sliderTicks as |label|}}
|
||||||
<span class="zap-tick">{{label}}</span>
|
<span>{{label}}</span>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if this.lnurlEndpoint}}
|
{{#if this.lnurlEndpoint}}
|
||||||
<div class="zap-min-max">
|
<div class="min-max">
|
||||||
Min:
|
Min:
|
||||||
{{this.minSats}}
|
{{this.minSats}}
|
||||||
sats · Max:
|
sats · Max:
|
||||||
@@ -362,9 +341,10 @@ export default class ZapPhotoModal extends Component {
|
|||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="zap-message-field">
|
<div class="form-group">
|
||||||
<label>Message (optional)</label>
|
<label>Message (optional)</label>
|
||||||
<textarea
|
<textarea
|
||||||
|
class="form-control"
|
||||||
rows="2"
|
rows="2"
|
||||||
placeholder="Say something nice..."
|
placeholder="Say something nice..."
|
||||||
aria-label="Zap message"
|
aria-label="Zap message"
|
||||||
@@ -373,7 +353,7 @@ export default class ZapPhotoModal extends Component {
|
|||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="zap-modal-actions">
|
<div class="edit-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
@@ -392,64 +372,57 @@ export default class ZapPhotoModal extends Component {
|
|||||||
sats
|
sats
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{! Step: Fetching Invoice }}
|
{{! Step: Fetching Invoice }}
|
||||||
{{#if (eq this.step "fetching-invoice")}}
|
{{#if (eq this.step "fetching-invoice")}}
|
||||||
<div class="zap-modal-body zap-modal-center">
|
<div class="centered">
|
||||||
<div class="zap-spinner"></div>
|
<Icon @name="loading-ring" @size={{32}} class="spin-animation" />
|
||||||
<p>Creating zap request and fetching invoice...</p>
|
<p>Creating zap request and fetching invoice...</p>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{! Step: Awaiting Payment }}
|
{{! Step: Awaiting Payment }}
|
||||||
{{#if (eq this.step "awaiting-payment")}}
|
{{#if (eq this.step "awaiting-payment")}}
|
||||||
<div class="zap-modal-body">
|
|
||||||
<p class="zap-pay-prompt">
|
<p class="zap-pay-prompt">
|
||||||
Scan to pay
|
Scan to pay
|
||||||
<strong>{{this.formattedAmount}} sats</strong>
|
<strong>{{this.formattedAmount}} sats</strong>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="zap-qr-container">
|
<div class="qr-code-container">
|
||||||
<canvas {{qrCode this.invoice}}></canvas>
|
<canvas {{qrCode this.invoice}}></canvas>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<a
|
<a href={{this.lightningUri}} class="btn btn-outline btn-full">
|
||||||
href={{this.lightningUri}}
|
|
||||||
class="btn btn-outline zap-open-wallet-link"
|
|
||||||
>
|
|
||||||
Open in Lightning wallet
|
Open in Lightning wallet
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{{#if this.hasWebLN}}
|
{{#if this.hasWebLN}}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-primary zap-webln-btn"
|
class="btn btn-primary btn-full webln-pay-btn"
|
||||||
{{on "click" this.performWebLNPay}}
|
{{on "click" this.performWebLNPay}}
|
||||||
>
|
>
|
||||||
Pay with WebLN
|
Pay with WebLN
|
||||||
</button>
|
</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="zap-invoice-details">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn-text"
|
class="btn-text copy-invoice-btn"
|
||||||
{{on "click" this.copyInvoice}}
|
{{on "click" this.copyInvoice}}
|
||||||
>
|
>
|
||||||
Copy invoice
|
Copy invoice
|
||||||
</button>
|
</button>
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#if this.paymentNotDetected}}
|
{{#if this.paymentNotDetected}}
|
||||||
<p class="zap-modal-hint">
|
<p class="meta-info">
|
||||||
Payment not detected yet. Keep the QR code open and try paying
|
Payment not detected yet. Keep the QR code open and try paying
|
||||||
again, or use a different Lightning wallet.
|
again, or use a different Lightning wallet.
|
||||||
</p>
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
<div class="zap-modal-actions">
|
<div class="edit-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
@@ -458,26 +431,24 @@ export default class ZapPhotoModal extends Component {
|
|||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{! Step: Paying with Wallet }}
|
{{! Step: Paying with Wallet }}
|
||||||
{{#if (eq this.step "paying-with-wallet")}}
|
{{#if (eq this.step "paying-with-wallet")}}
|
||||||
<div class="zap-modal-body zap-modal-center">
|
<div class="centered">
|
||||||
<div class="zap-spinner"></div>
|
<Icon @name="loading-ring" @size={{32}} class="spin-animation" />
|
||||||
<p>Paying invoice with WebLN...</p>
|
<p>Paying invoice with WebLN...</p>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{! Step: Wallet Pay Error }}
|
{{! Step: Wallet Pay Error }}
|
||||||
{{#if (eq this.step "wallet-pay-error")}}
|
{{#if (eq this.step "wallet-pay-error")}}
|
||||||
<div class="zap-modal-body">
|
<div class="alert alert-error">{{this.error}}</div>
|
||||||
<div class="zap-modal-error-msg">{{this.error}}</div>
|
<p class="meta-info">
|
||||||
<p class="zap-modal-hint">
|
|
||||||
The WebLN payment failed. You can still scan the QR code with a
|
The WebLN payment failed. You can still scan the QR code with a
|
||||||
Lightning wallet.
|
Lightning wallet.
|
||||||
</p>
|
</p>
|
||||||
<div class="zap-modal-actions">
|
<div class="edit-actions">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
@@ -493,13 +464,12 @@ export default class ZapPhotoModal extends Component {
|
|||||||
Show Invoice
|
Show Invoice
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{! Step: Success }}
|
{{! Step: Success }}
|
||||||
{{#if (eq this.step "success")}}
|
{{#if (eq this.step "success")}}
|
||||||
<div class="zap-modal-body zap-modal-center zap-success">
|
<div class="success zap-success">
|
||||||
<div class="zap-success-icon">⚡</div>
|
<div class="success-icon">⚡</div>
|
||||||
<h4>Zap Sent!</h4>
|
<h4>Zap Sent!</h4>
|
||||||
<p>
|
<p>
|
||||||
You zapped
|
You zapped
|
||||||
@@ -520,9 +490,8 @@ export default class ZapPhotoModal extends Component {
|
|||||||
|
|
||||||
{{! Step: Error }}
|
{{! Step: Error }}
|
||||||
{{#if (eq this.step "error")}}
|
{{#if (eq this.step "error")}}
|
||||||
<div class="zap-modal-body">
|
<div class="alert alert-error">{{this.error}}</div>
|
||||||
<div class="zap-modal-error-msg">{{this.error}}</div>
|
<div class="edit-actions">
|
||||||
<div class="zap-modal-actions">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="btn btn-outline"
|
class="btn btn-outline"
|
||||||
@@ -538,9 +507,8 @@ export default class ZapPhotoModal extends Component {
|
|||||||
Try Again
|
Try Again
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
}
|
}
|
||||||
|
|||||||
+57
-203
@@ -2427,79 +2427,69 @@ button.create-place {
|
|||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Zap Photo Modal */
|
/* Generic modal heading reset (shared by all modals) */
|
||||||
.zap-photo-modal-overlay {
|
.modal-content h2,
|
||||||
position: fixed;
|
.modal-content h3 {
|
||||||
inset: 0;
|
margin-top: 0;
|
||||||
background: rgb(0 0 0 / 70%);
|
|
||||||
z-index: 10000;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
padding: 1rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Zap Photo Modal — scoped, nested styles */
|
||||||
.zap-photo-modal {
|
.zap-photo-modal {
|
||||||
background: white;
|
text-align: center;
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 8px 32px rgb(0 0 0 / 30%);
|
& h2 {
|
||||||
width: 100%;
|
margin-bottom: 1rem;
|
||||||
max-width: 420px;
|
|
||||||
max-height: 90vh;
|
|
||||||
overflow-y: auto;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-photo-modal-header {
|
& .amount-display {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: baseline;
|
||||||
justify-content: space-between;
|
|
||||||
padding: 1rem 1.25rem;
|
|
||||||
border-bottom: 1px solid var(--divider-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-photo-modal-header h3 {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-close {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
padding: 0.25rem;
|
|
||||||
cursor: pointer;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
color: #898989;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-close:hover {
|
|
||||||
color: var(--body-text-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-status {
|
|
||||||
padding: 0.75rem 1.25rem;
|
|
||||||
border-bottom: 1px solid var(--divider-color);
|
|
||||||
min-height: 2.5rem;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 0.4rem;
|
gap: 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-loading {
|
& .amount-value {
|
||||||
font-size: 0.85rem;
|
font-size: 2rem;
|
||||||
color: #898989;
|
font-weight: 700;
|
||||||
|
color: var(--default-list-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-address {
|
& .amount-unit {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--body-text-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .zap-slider {
|
||||||
|
width: 100%;
|
||||||
|
accent-color: var(--default-list-color);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .slider-ticks {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--body-text-color);
|
||||||
|
opacity: 0.5;
|
||||||
|
padding: 0 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .min-max {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--body-text-color);
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
& .lightning-address {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-badge {
|
& .lightning-badge {
|
||||||
background: #f7b500;
|
background: var(--default-list-color);
|
||||||
color: white;
|
color: white;
|
||||||
font-size: 0.7rem;
|
font-size: 0.7rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
@@ -2509,177 +2499,41 @@ button.create-place {
|
|||||||
letter-spacing: 0.5px;
|
letter-spacing: 0.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-address-text {
|
& .lightning-address-text {
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
font-size: 0.8rem;
|
font-size: 0.8rem;
|
||||||
color: #555;
|
color: var(--body-text-color);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-error-msg {
|
& .centered {
|
||||||
color: var(--danger-color);
|
|
||||||
font-size: 0.85rem;
|
|
||||||
padding: 0.5rem 0.75rem;
|
|
||||||
background: rgb(234 67 53 / 8%);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-body {
|
|
||||||
padding: 1.25rem;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
gap: 1rem;
|
gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-modal-center {
|
& .success {
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-amount-display {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 0.4rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-amount-value {
|
|
||||||
font-size: 2rem;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #f7b500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-amount-unit {
|
|
||||||
font-size: 0.9rem;
|
|
||||||
color: #898989;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-slider {
|
|
||||||
width: 100%;
|
|
||||||
accent-color: #f7b500;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-slider-ticks {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
font-size: 0.7rem;
|
|
||||||
color: #aaa;
|
|
||||||
padding: 0 0.25rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-min-max {
|
|
||||||
font-size: 0.75rem;
|
|
||||||
color: #898989;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-message-field {
|
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 0.3rem;
|
align-items: center;
|
||||||
}
|
|
||||||
|
|
||||||
.zap-message-field label {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #898989;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-message-field textarea {
|
|
||||||
width: 100%;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 0.5rem;
|
|
||||||
font-family: inherit;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
resize: vertical;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-message-field textarea:focus {
|
|
||||||
outline: none;
|
|
||||||
border-color: var(--link-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 0.5rem;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-modal-hint {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
color: #898989;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-spinner {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
border: 3px solid #eee;
|
|
||||||
border-top-color: #f7b500;
|
|
||||||
border-radius: 50%;
|
|
||||||
animation: zap-spin 0.8s linear infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes zap-spin {
|
|
||||||
to {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-pay-prompt {
|
|
||||||
text-align: center;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-qr-container {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-qr-container canvas {
|
|
||||||
border-radius: 8px;
|
|
||||||
background: white;
|
|
||||||
padding: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-qr-container a {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-webln-btn {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-open-wallet-link {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-invoice-details {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
font-size: 0.8rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zap-success {
|
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-success-icon {
|
& .success-icon {
|
||||||
font-size: 3rem;
|
font-size: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-success h4 {
|
& .success h4 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 1.2rem;
|
font-size: 1.2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.zap-success p {
|
& .success p {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font-size: 0.9rem;
|
font-size: 0.9rem;
|
||||||
color: #555;
|
color: var(--body-text-color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert.dom('.zap-slider').exists('slider is rendered');
|
assert.dom('.zap-slider').exists('slider is rendered');
|
||||||
assert.dom('.zap-amount-value').exists('amount is displayed');
|
assert.dom('.zap-amount').exists('amount is displayed');
|
||||||
assert
|
assert
|
||||||
.dom('.zap-modal-address-text')
|
.dom('.lightning-address-text')
|
||||||
.hasText('user@example.com', 'lightning address is shown');
|
.hasText('user@example.com', 'lightning address is shown');
|
||||||
assert.dom('.zap-modal-badge').hasText('Lightning', 'badge is shown');
|
assert.dom('.lightning-badge').hasText('Lightning', 'badge is shown');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it shows error when user has no lightning address', async function (assert) {
|
test('it shows error when user has no lightning address', async function (assert) {
|
||||||
@@ -60,10 +60,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('.zap-modal-error-msg')
|
.dom('.alert-error')
|
||||||
.exists('error message is shown when no lightning address');
|
.exists('error message is shown when no lightning address');
|
||||||
assert
|
assert
|
||||||
.dom('.zap-modal-actions .btn-primary')
|
.dom('.edit-actions .btn-primary')
|
||||||
.isDisabled('zap button is disabled when no address');
|
.isDisabled('zap button is disabled when no address');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -80,11 +80,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert.dom('.alert-error').exists('error shown when allowsNostr is false');
|
||||||
assert
|
assert
|
||||||
.dom('.zap-modal-error-msg')
|
.dom('.edit-actions .btn-primary')
|
||||||
.exists('error shown when allowsNostr is false');
|
|
||||||
assert
|
|
||||||
.dom('.zap-modal-actions .btn-primary')
|
|
||||||
.isDisabled('zap button is disabled');
|
.isDisabled('zap button is disabled');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -103,10 +101,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
assert
|
assert
|
||||||
.dom('.zap-modal-actions .btn-primary')
|
.dom('.edit-actions .btn-primary')
|
||||||
.isNotDisabled('zap button is enabled when endpoint is valid');
|
.isNotDisabled('zap button is enabled when endpoint is valid');
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-primary');
|
await click('.edit-actions .btn-primary');
|
||||||
|
|
||||||
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
|
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
@@ -120,9 +118,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
'zap amount is 1000 sats in millisats'
|
'zap amount is 1000 sats in millisats'
|
||||||
);
|
);
|
||||||
|
|
||||||
await waitFor('.zap-qr-container');
|
await waitFor('.qr-code-container');
|
||||||
|
|
||||||
assert.dom('.zap-qr-container').exists('QR code is shown');
|
assert.dom('.qr-code-container').exists('QR code is shown');
|
||||||
assert.dom('.zap-pay-prompt').exists('payment prompt is shown');
|
assert.dom('.zap-pay-prompt').exists('payment prompt is shown');
|
||||||
assert.notOk(closed, 'modal was not closed by clicking the Zap button');
|
assert.notOk(closed, 'modal was not closed by clicking the Zap button');
|
||||||
});
|
});
|
||||||
@@ -137,9 +135,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await fillIn('.zap-message-field textarea', 'Great photo!');
|
await fillIn('.form-group textarea', 'Great photo!');
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-primary');
|
await click('.edit-actions .btn-primary');
|
||||||
|
|
||||||
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
|
assert.ok(zapSpy.calledOnce, 'nostrZap.zap was called');
|
||||||
assert.strictEqual(
|
assert.strictEqual(
|
||||||
@@ -159,10 +157,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-primary');
|
await click('.edit-actions .btn-primary');
|
||||||
await waitFor('.zap-webln-btn');
|
await waitFor('.webln-pay-btn');
|
||||||
|
|
||||||
assert.dom('.zap-webln-btn').exists('WebLN button is shown when available');
|
assert.dom('.webln-pay-btn').exists('WebLN button is shown when available');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it shows success after WebLN payment', async function (assert) {
|
test('it shows success after WebLN payment', async function (assert) {
|
||||||
@@ -175,9 +173,9 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-primary');
|
await click('.edit-actions .btn-primary');
|
||||||
await waitFor('.zap-webln-btn');
|
await waitFor('.webln-pay-btn');
|
||||||
await click('.zap-webln-btn');
|
await click('.webln-pay-btn');
|
||||||
|
|
||||||
await waitFor('.zap-success');
|
await waitFor('.zap-success');
|
||||||
|
|
||||||
@@ -193,10 +191,10 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-primary');
|
await click('.edit-actions .btn-primary');
|
||||||
await waitFor('.zap-qr-container');
|
await waitFor('.qr-code-container');
|
||||||
|
|
||||||
assert.dom('.zap-qr-container').exists('QR code is shown');
|
assert.dom('.qr-code-container').exists('QR code is shown');
|
||||||
|
|
||||||
assert.ok(
|
assert.ok(
|
||||||
this.nostrZap._receiptCallback,
|
this.nostrZap._receiptCallback,
|
||||||
@@ -227,7 +225,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-modal-close');
|
await click('.close-modal-btn');
|
||||||
|
|
||||||
assert.ok(closed, 'onClose was called');
|
assert.ok(closed, 'onClose was called');
|
||||||
});
|
});
|
||||||
@@ -245,7 +243,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-modal-actions .btn-outline');
|
await click('.edit-actions .btn-outline');
|
||||||
|
|
||||||
assert.ok(closed, 'onClose was called from Cancel button');
|
assert.ok(closed, 'onClose was called from Cancel button');
|
||||||
});
|
});
|
||||||
@@ -268,7 +266,7 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
assert.notOk(closed, 'onClose was not called when clicking inside modal');
|
assert.notOk(closed, 'onClose was not called when clicking inside modal');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it does not close when clicking the overlay background', async function (assert) {
|
test('it closes when clicking the overlay background', async function (assert) {
|
||||||
let closed = false;
|
let closed = false;
|
||||||
this.handleClose = () => {
|
this.handleClose = () => {
|
||||||
closed = true;
|
closed = true;
|
||||||
@@ -281,8 +279,8 @@ module('Integration | Component | zap-photo-modal', function (hooks) {
|
|||||||
</template>
|
</template>
|
||||||
);
|
);
|
||||||
|
|
||||||
await click('.zap-photo-modal-overlay');
|
await click('.modal-overlay');
|
||||||
|
|
||||||
assert.notOk(closed, 'onClose was not called when clicking overlay');
|
assert.ok(closed, 'onClose was called when clicking overlay');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user