@12-apps/payments-frontend 3.39.0 → 3.41.0
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/package.json +2 -2
- package/src/activation/use-redirect-activation.ts +22 -1
- package/src/components/PaymentProviderSettings.tsx +13 -0
- package/src/components/checkout/list/bank-apps.ts +113 -0
- package/src/components/checkout/list/bank-en-US.ts +26 -0
- package/src/components/checkout/list/bank-logos.tsx +35 -0
- package/src/components/checkout/list/bank-pt-BR.ts +26 -0
- package/src/components/checkout/list/choice.ts +8 -3
- package/src/components/checkout/list/context.tsx +27 -3
- package/src/components/checkout/list/list-face.tsx +55 -15
- package/src/components/checkout/list/list-memory.ts +96 -0
- package/src/components/checkout/list/list-pix.tsx +151 -15
- package/src/components/checkout/list/payment-list-step.tsx +1 -0
- package/src/components/checkout/list/pix-banks.tsx +178 -0
- package/src/components/checkout/list/types.ts +91 -0
- package/src/components/checkout/list/use-list-state.ts +6 -3
- package/src/components/checkout/pix-after-copy.tsx +8 -2
- package/src/components/checkout/pix-parts.tsx +56 -0
- package/src/components/checkout/pix-view.tsx +2 -0
- package/src/components/settings-live.ts +66 -0
- package/src/flows/create-payments-settings.tsx +6 -2
- package/src/index.ts +2 -2
- package/src/locales.ts +12 -1
|
@@ -82,6 +82,62 @@ export function useClipboardCopy(text: string): { copy: () => Promise<boolean>;
|
|
|
82
82
|
return { copy, justCopied };
|
|
83
83
|
}
|
|
84
84
|
|
|
85
|
+
/** A copy opened inside a tap whose text arrives later (FUT-3612) — see {@link deferredCopy}. */
|
|
86
|
+
export interface DeferredCopy {
|
|
87
|
+
/** The text has arrived: write it. */
|
|
88
|
+
resolve: (text: string) => void;
|
|
89
|
+
/** There will be no text (the raise was refused): give the clipboard up. */
|
|
90
|
+
fail: () => void;
|
|
91
|
+
/** `true` once the clipboard holds the text; `false` when it refused or there was none. */
|
|
92
|
+
done: Promise<boolean>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Open the clipboard NOW, inside the buyer's tap, for a text the server has
|
|
97
|
+
* not answered yet. Safari lets a page write to the clipboard only inside the
|
|
98
|
+
* gesture, so the write is started here with a promise of the text
|
|
99
|
+
* (`ClipboardItem`); where that is missing or refused, the plain write is
|
|
100
|
+
* tried once the text arrives — Chrome takes it from a focused page.
|
|
101
|
+
*/
|
|
102
|
+
export function deferredCopy(): DeferredCopy {
|
|
103
|
+
let give: (text: string | null) => void = () => undefined;
|
|
104
|
+
const text = new Promise<string | null>((resolve) => {
|
|
105
|
+
give = resolve;
|
|
106
|
+
});
|
|
107
|
+
const plain = async (): Promise<boolean> => {
|
|
108
|
+
const value = await text;
|
|
109
|
+
if (value === null || !navigator.clipboard) return false;
|
|
110
|
+
try {
|
|
111
|
+
await navigator.clipboard.writeText(value);
|
|
112
|
+
return true;
|
|
113
|
+
} catch {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
const Item = typeof window === "undefined" ? undefined : (window as { ClipboardItem?: typeof ClipboardItem }).ClipboardItem;
|
|
118
|
+
let done: Promise<boolean> = Promise.resolve(false);
|
|
119
|
+
let opened = false;
|
|
120
|
+
if (Item && navigator.clipboard && typeof navigator.clipboard.write === "function") {
|
|
121
|
+
const blob = text.then((value) => {
|
|
122
|
+
if (value === null) throw new Error("no text");
|
|
123
|
+
return new Blob([value], { type: "text/plain" });
|
|
124
|
+
});
|
|
125
|
+
// An unanswered blob must not surface as an unhandled rejection.
|
|
126
|
+
blob.catch(() => undefined);
|
|
127
|
+
try {
|
|
128
|
+
done = navigator.clipboard.write([new Item({ "text/plain": blob })]).then(
|
|
129
|
+
() => true,
|
|
130
|
+
async () => (await text) !== null && plain(),
|
|
131
|
+
);
|
|
132
|
+
opened = true;
|
|
133
|
+
} catch {
|
|
134
|
+
// A browser whose ClipboardItem takes no promise throws here, in the tap.
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (!opened) done = plain();
|
|
138
|
+
return { resolve: (value) => give(value), fail: () => give(null), done };
|
|
139
|
+
}
|
|
140
|
+
|
|
85
141
|
/**
|
|
86
142
|
* A PIX's QR or code, veiled while the cart has changed under it (FUT-3535):
|
|
87
143
|
* blurred past reading, out of reach of a pointer, a selection and a screen
|
|
@@ -287,6 +287,8 @@ function PixBody({ order, pix, poll }: { order: CheckoutOrder; pix: PixCharge; p
|
|
|
287
287
|
onShowQr={() => setAfterCopy(false)}
|
|
288
288
|
onPreferCard={stage.preferCard}
|
|
289
289
|
footer={poll.error || poll.timedOut ? footerLine(copy.verifying) : null}
|
|
290
|
+
writeClipboard={writeClipboard}
|
|
291
|
+
setAfterCopy={setAfterCopy}
|
|
290
292
|
/>
|
|
291
293
|
);
|
|
292
294
|
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef } from 'react';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The host's realtime channel for the settings screen (FUT-648).
|
|
7
|
+
*
|
|
8
|
+
* A provider's connection moves while nobody touches this tab: the OAuth grant
|
|
9
|
+
* completes in the tab the owner sent to the provider, a colleague saves a key
|
|
10
|
+
* or reorders the chain, a webhook or a sweep settles the activation charge.
|
|
11
|
+
* The screen read its view once on mount and then sat on it, so all of that
|
|
12
|
+
* reached the owner only through a reload.
|
|
13
|
+
*
|
|
14
|
+
* Nothing here knows what the channel is — the same split as the checkout's
|
|
15
|
+
* `CheckoutLiveSignal`. A host that can hear "this store's payment settings
|
|
16
|
+
* moved" passes whether its channel is live and a way to be told when it
|
|
17
|
+
* fires; the screen re-reads its view on every hint and once each time the
|
|
18
|
+
* channel (re)opens, because a hint published while it was down reached
|
|
19
|
+
* nobody. The hint carries nothing and is never trusted as data: the re-read
|
|
20
|
+
* goes through the same client, which is the only thing that decides what the
|
|
21
|
+
* owner may see.
|
|
22
|
+
*
|
|
23
|
+
* There is no timer, live or not. The screen never polled, so a host without a
|
|
24
|
+
* channel — or one whose channel is down — keeps exactly the mount read and its
|
|
25
|
+
* own actions' re-reads, as before.
|
|
26
|
+
*/
|
|
27
|
+
export interface PaymentsSettingsLiveSignal {
|
|
28
|
+
/** Whether the host's channel is connected right now. */
|
|
29
|
+
live: boolean;
|
|
30
|
+
/**
|
|
31
|
+
* Be told when the channel says this store's payment settings moved.
|
|
32
|
+
* Returns the unsubscribe. Should be stable across renders: a new function
|
|
33
|
+
* re-subscribes.
|
|
34
|
+
*/
|
|
35
|
+
subscribe: (onHint: () => void) => () => void;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Re-read on every hint, and once whenever the channel goes from not-live to
|
|
40
|
+
* live — the first connect after mount included, since the mount read may
|
|
41
|
+
* have been answered before the subscription existed.
|
|
42
|
+
*
|
|
43
|
+
* `reload` is read through a ref so a host re-rendering with a new function
|
|
44
|
+
* neither re-subscribes nor re-reads.
|
|
45
|
+
*/
|
|
46
|
+
export function useSettingsLive(
|
|
47
|
+
signal: PaymentsSettingsLiveSignal | undefined,
|
|
48
|
+
reload: () => void,
|
|
49
|
+
): void {
|
|
50
|
+
const latest = useRef(reload);
|
|
51
|
+
latest.current = reload;
|
|
52
|
+
const live = signal?.live === true;
|
|
53
|
+
const wasLive = useRef(live);
|
|
54
|
+
const subscribe = signal?.subscribe;
|
|
55
|
+
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
const opened = live && !wasLive.current;
|
|
58
|
+
wasLive.current = live;
|
|
59
|
+
if (opened) latest.current();
|
|
60
|
+
}, [live]);
|
|
61
|
+
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (!subscribe) return undefined;
|
|
64
|
+
return subscribe(() => latest.current());
|
|
65
|
+
}, [subscribe]);
|
|
66
|
+
}
|
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
* settings page behind.
|
|
19
19
|
*
|
|
20
20
|
* **`prepareConnect`, `selectedProvider` / `onProviderChange`,
|
|
21
|
-
* `initialProvider`, `onChanged`.** The OAuth CSRF state is minted against
|
|
21
|
+
* `initialProvider`, `onChanged`, `live`.** The OAuth CSRF state is minted against
|
|
22
22
|
* the host's admin session; which provider is open may live in the host's
|
|
23
23
|
* URL. Both are the host's, per render, and a factory that froze them would
|
|
24
|
-
* make the page uncontrollable.
|
|
24
|
+
* make the page uncontrollable. `live` is the host's realtime channel, whose
|
|
25
|
+
* status changes from render to render (FUT-648).
|
|
25
26
|
*/
|
|
26
27
|
|
|
27
28
|
import type { ComponentType, JSX } from "react";
|
|
@@ -32,6 +33,9 @@ import {
|
|
|
32
33
|
} from "../components/PaymentProviderSettings";
|
|
33
34
|
import type { PaymentsSettingsClient } from "../client";
|
|
34
35
|
|
|
36
|
+
/** The `live` prop's shape — the host's channel, see `../components/settings-live`. */
|
|
37
|
+
export type { PaymentsSettingsLiveSignal } from "../components/settings-live";
|
|
38
|
+
|
|
35
39
|
/** What a host binds ONCE: the settings port. */
|
|
36
40
|
export interface PaymentsSettingsWebConfig {
|
|
37
41
|
/**
|
package/src/index.ts
CHANGED
|
@@ -56,7 +56,7 @@ export { PT_BR_CHECKOUT_PAYMENT_COPY } from './components/checkout-payment-pt-BR
|
|
|
56
56
|
// ---------------------------------------------------------------------------
|
|
57
57
|
export { CheckoutFlow, type CheckoutCartView, type CheckoutFlowProps } from './components/checkout/checkout-flow';
|
|
58
58
|
// The Pagamento step as ONE list (FUT-3591/3592) — opt-in via `CheckoutFlowProps.paymentList`.
|
|
59
|
-
export type { CheckoutDoorOption, CheckoutListChoice, CheckoutListHold, CheckoutOnlineChoice, CheckoutPaymentList, PaymentListCopy } from './components/checkout/list/types';
|
|
59
|
+
export type { CheckoutDoorOption, CheckoutListChoice, CheckoutListHold, CheckoutOnlineChoice, CheckoutPaymentList, CheckoutPixBank, PaymentListCopy, PixBankCopy, PixBankId, PixBankName } from './components/checkout/list/types';
|
|
60
60
|
export { PT_BR_PAYMENT_LIST_COPY } from './components/checkout/list/pt-BR';
|
|
61
61
|
export type { BackActionEmphasis } from './components/checkout/payment-status-parts';
|
|
62
62
|
export type { CheckoutDeliverySummary, CheckoutSummaryLine } from './components/checkout/payment-summary';
|
|
@@ -323,7 +323,7 @@ export {
|
|
|
323
323
|
type PaymentProviderSettingsProps,
|
|
324
324
|
} from './components/PaymentProviderSettings';
|
|
325
325
|
export { createWebPaymentsSettings } from './flows/create-payments-settings';
|
|
326
|
-
export type { BoundPaymentsSettingsProps, PaymentsSettingsSurface, PaymentsSettingsWebConfig } from './flows/create-payments-settings';
|
|
326
|
+
export type { BoundPaymentsSettingsProps, PaymentsSettingsLiveSignal, PaymentsSettingsSurface, PaymentsSettingsWebConfig } from './flows/create-payments-settings';
|
|
327
327
|
|
|
328
328
|
// ---------------------------------------------------------------------------
|
|
329
329
|
// The PLATFORM operations screens (FUT-479 / FUT-483, packaged by FUT-573) —
|
package/src/locales.ts
CHANGED
|
@@ -22,7 +22,9 @@ import type { CheckoutScreensCopy } from './components/checkout/screens-copy';
|
|
|
22
22
|
import { PT_BR_CHECKOUT_SCREENS_COPY } from './components/checkout/screens-pt-BR';
|
|
23
23
|
import { EN_US_PAYMENT_LIST_COPY } from './components/checkout/list/en-US';
|
|
24
24
|
import { PT_BR_PAYMENT_LIST_COPY } from './components/checkout/list/pt-BR';
|
|
25
|
-
import
|
|
25
|
+
import { EN_US_PIX_BANK_COPY } from './components/checkout/list/bank-en-US';
|
|
26
|
+
import { PT_BR_PIX_BANK_COPY } from './components/checkout/list/bank-pt-BR';
|
|
27
|
+
import type { PaymentListCopy, PixBankCopy } from './components/checkout/list/types';
|
|
26
28
|
import type { CheckoutViewCopy, PaymentStatusCopy } from './components/checkout/view-copy';
|
|
27
29
|
import type { PlatformHomologacaoCopy } from './components/platform/copy';
|
|
28
30
|
import { EN_US_PLATFORM_HOMOLOGACAO_COPY } from './components/platform/en-US';
|
|
@@ -88,6 +90,15 @@ export const PAYMENT_LIST_COPY = {
|
|
|
88
90
|
'en-US': EN_US_PAYMENT_LIST_COPY,
|
|
89
91
|
} as const satisfies LocalePack<PaymentListCopy>;
|
|
90
92
|
|
|
93
|
+
/**
|
|
94
|
+
* The Pix-to-bank flow's words (FUT-3612) — `CheckoutFlowProps.paymentList.pixBank.copy`.
|
|
95
|
+
* Read by the SHOPPER.
|
|
96
|
+
*/
|
|
97
|
+
export const PIX_BANK_COPY = {
|
|
98
|
+
'pt-BR': PT_BR_PIX_BANK_COPY,
|
|
99
|
+
'en-US': EN_US_PIX_BANK_COPY,
|
|
100
|
+
} as const satisfies LocalePack<PixBankCopy>;
|
|
101
|
+
|
|
91
102
|
export const CHECKOUT_COPY = {
|
|
92
103
|
'pt-BR': PT_BR_CHECKOUT_COPY,
|
|
93
104
|
'en-US': EN_US_CHECKOUT_COPY,
|