Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions cdn/src/paywall.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { sleep } from '@shared/utils'
import { Paywall } from '@tools/components'
import {
fetchProfile,
fetchQuote,
getScriptParams,
getWallet,
initiatePayment,
Expand Down Expand Up @@ -35,10 +34,6 @@ function drawPaywall() {
// TODO: create and call API
},
getWallet: (walletAddressUrl) => getWallet(API_URL, walletAddressUrl),
fetchQuote({ sender, receiver, amount }) {
const receiveAmount = Number(amount)
return fetchQuote(API_URL, { sender, receiver, receiveAmount })
},
async initiatePayment({ sender, receiver, amount, note }) {
const receiveAmount = Number(amount)
const redirectUrl = window.location.href
Expand Down
35 changes: 35 additions & 0 deletions components/src/paywall/components/common.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
.title {
font-size: 1.75em;
}

.description {
font-size: 1em;
}

button {
border: none;
display: flex;
justify-content: center;
align-items: center;
padding: 1em;
font-size: 1.25em;
border-radius: var(--border-radius);
background-color: var(--theme);
/* contrast-color() is unreliable in Chrome shadow DOM; approximate with OKLCH:
https://css-tricks.com/approximating-contrast-color-with-other-css-features/ */
color: white;
color: oklch(from var(--theme) round(1.21 - L) 0 0);
font-weight: 600;
cursor: pointer;
}

button:disabled {
opacity: 0.75;
cursor: not-allowed;
}

.footer {
font-size: 0.6em;
margin-top: -1.25em;
text-align: center;
}
47 changes: 47 additions & 0 deletions components/src/paywall/components/form.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
:host {
display: flex;
flex-direction: column;
gap: 1em;
width: 100%;
height: 100%;
}

form {
display: flex;
flex-direction: column;
gap: 1em;
margin-top: auto;
}

form label {
display: block;
margin-bottom: 0.2em;
font-size: 1em;
font-weight: 600;
}

form input {
font-family: inherit;
border: 2px solid rgba(0, 0, 0, 0.1);
display: flex;
width: 100%;
justify-content: center;
align-items: center;
padding: 1em;
font-size: 1.25em;
border-radius: var(--border-radius);
background-color: #fff;
}

form wm-dots-loader {
--primary-color: var(--theme);
}

form input[aria-invalid='true'] {
border-color: var(--color-error);
}
form div p.error {
margin-top: 0.2em;
font-size: 0.9em;
color: var(--color-error);
}
90 changes: 90 additions & 0 deletions components/src/paywall/components/form.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { html, LitElement, unsafeCSS } from 'lit'
import { property, state } from 'lit/decorators.js'
import { DotsLoader } from '@c/shared/dots-loader'
import { registerComponents } from '@c/utils'
import stylesCommon from './common.css?raw'
import styles from './form.css?raw'
import { DEFAULTS } from '../utils'
import styleTokens from '../vars.css?raw'

export type FormSubmitEventDetail = {
walletAddress: string
onComplete(error?: string): void
}

export class PaywallWalletAddressForm extends LitElement {
static styles = [
unsafeCSS(styleTokens),
unsafeCSS(stylesCommon),
unsafeCSS(styles),
]

@property({ type: String }) title = DEFAULTS.title.text
@property({ type: String }) description = DEFAULTS.description.text
@property({ type: String }) ctaText = DEFAULTS.ctaButton.text

@state() private _error = ''
@state() private _loading = false

connectedCallback() {
super.connectedCallback()
registerComponents({
'wm-dots-loader': DotsLoader,
})
}

render() {
return html`
<h2 class="title">${this.title}</h2>
<p class="description">${this.description}</p>

<form @submit=${this.handleSubmit}>
<div>
<label for="wallet-address-url">Wallet address</label>
<input
type="text"
id="wallet-address-url"
name="walletAddress"
placeholder="https://walletprovider.com/MyWallet"
aria-invalid=${!!this._error}
aria-describedby=${this._error ? 'wallet-address-url-error' : null}
?disabled=${this._loading}
required
/>
<p id="wallet-address-url-error" class="error">
${this._error}&nbsp;
</p>
</div>

<button type="submit" ?disabled=${this._loading}>
${this._loading
? html`<wm-dots-loader></wm-dots-loader
><span class="sr-only">${this.ctaText}</span>`
: this.ctaText}
</button>
</form>
`
}

private async handleSubmit(ev: SubmitEvent) {
ev.preventDefault()
this._error = ''
const formData = new FormData(ev.target as HTMLFormElement)
const walletAddress = formData.get('walletAddress') as string

if (!walletAddress) {
this._error = 'Please enter a valid wallet address'
return
}

this._loading = true
const detail: FormSubmitEventDetail = {
walletAddress,
onComplete: (error) => {
this._error = error || ''
this._loading = false
},
}
this.dispatchEvent(new CustomEvent('submit', { detail }))
}
}
31 changes: 0 additions & 31 deletions components/src/paywall/components/home.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,6 @@
height: 100%;
}

.title {
font-size: 1.75em;
}

.description {
font-size: 1em;
}

.price {
background-color: rgba(0, 0, 0, 0.05);
border: rgba(0, 0, 0, 0.1);
Expand All @@ -33,26 +25,3 @@
font-weight: 600;
}
}

button {
border: none;
display: flex;
justify-content: center;
align-items: center;
padding: 1em;
font-size: 1.25em;
border-radius: var(--border-radius);
background-color: var(--theme);
/* contrast-color() is unreliable in Chrome shadow DOM; approximate with OKLCH:
https://css-tricks.com/approximating-contrast-color-with-other-css-features/ */
color: white;
color: oklch(from var(--theme) round(1.21 - L) 0 0);
font-weight: 600;
cursor: pointer;
}

.footer {
font-size: 0.6em;
margin-top: -1.25em;
text-align: center;
}
17 changes: 12 additions & 5 deletions components/src/paywall/components/home.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { html, LitElement, unsafeCSS } from 'lit'
import { property } from 'lit/decorators.js'
import { createDefaultPaywallProfile } from '@shared/default-data'
import { formatCurrency } from '@shared/utils'
import stylesCommon from './common.css?raw'
import styles from './home.css?raw'
import { DEFAULTS } from '../utils'
import styleTokens from '../vars.css?raw'

const DEFAULTS = createDefaultPaywallProfile('')

export class PaywallHome extends LitElement {
static styles = [unsafeCSS(styleTokens), unsafeCSS(styles)]
static styles = [
unsafeCSS(styleTokens),
unsafeCSS(stylesCommon),
unsafeCSS(styles),
]

@property({ type: Object, attribute: false })
price: PaymentCurrencyAmount = DEFAULTS.price
Expand All @@ -25,8 +28,12 @@ export class PaywallHome extends LitElement {
<div class="price">
<span>Unlock</span> <span>${formatCurrency(this.price)}</span>
</div>
<button type="button">${this.ctaText}</button>
<button type="button" @click=${this.onClick}>${this.ctaText}</button>
<p class="footer">Secured by Open Payments. No card needed</p>
`
}

private onClick() {
this.dispatchEvent(new CustomEvent('payStart'))
}
}
18 changes: 2 additions & 16 deletions components/src/paywall/controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,6 @@ type WalletAddressUrl = string
/** The amount sender wants to send (like "1.05"), does not include fees */
type UserAmount = number | PaymentCurrencyAmount['value']

interface QuoteInput {
sender: WalletAddressInfo
receiver: WalletAddressInfo
amount: UserAmount
}
type QuoteResult =
| { debitAmount: PaymentCurrencyAmount; receiveAmount: PaymentCurrencyAmount }
| { error: string; minSendAmount?: PaymentCurrencyAmount }

interface InitiatePaymentInput {
sender: WalletAddressInfo
receiver: WalletAddressInfo
Expand All @@ -29,6 +20,8 @@ interface InitiatePaymentResult {

type Entitlement = 'no-access' | 'auth-required' | 'has-access'

export type Screens = 'home' | 'form'

export interface Controller {
receiverWalletAddressUrl: string
cdnUrl: string
Expand All @@ -48,7 +41,6 @@ export interface Controller {
): Promise<void>

getWallet(walletAddressUrl: WalletAddressUrl): Promise<WalletAddressInfo>
fetchQuote(request: QuoteInput): Promise<QuoteResult>
initiatePayment(request: InitiatePaymentInput): Promise<InitiatePaymentResult>
getStatus(
paymentId: string,
Expand All @@ -75,12 +67,6 @@ export const NO_OP_CONTROLLER: Controller = {
publicName: 'Wallet (Preview)',
})
},
fetchQuote({ amount, sender, receiver }) {
amount = String(amount)
const debitAmount = { value: amount, currency: sender.assetCode }
const receiveAmount = { value: amount, currency: receiver.assetCode }
return Promise.resolve({ debitAmount, receiveAmount })
},
initiatePayment() {
return Promise.resolve({
paymentId: 'payment-id',
Expand Down
Loading
Loading