|
1 | 1 | import { PaymentElement, useCheckout } from "@stripe/react-stripe-js" |
2 | 2 | import classNames from "classnames" |
3 | 3 | import Link from "next/link" |
4 | | -import { ChangeEvent, FormEvent, useCallback, useState } from "react" |
| 4 | +import { ChangeEvent, FormEvent, useCallback, useEffect, useState } from "react" |
5 | 5 | import { FormattedMessage } from "react-intl" |
6 | 6 |
|
7 | 7 | import LoadingIcon from "../../public/icons/loading.svg?inline" |
8 | 8 | import ArrowLeftIcon from "../../public/ui/arrow-left.svg?inline" |
| 9 | +import ExternalLinkIcon from "../../public/ui/external-link.svg?inline" |
9 | 10 |
|
10 | 11 | import { Button } from "../Button" |
11 | 12 | import { Input } from "../Input" |
| 13 | +import { isInIframe } from "../../donate/utils" |
12 | 14 |
|
13 | 15 | interface DonateCheckoutProps { |
14 | 16 | backUrl?: string |
@@ -72,47 +74,66 @@ export function DonateCheckout({ |
72 | 74 | [checkout, email, onComplete] |
73 | 75 | ) |
74 | 76 |
|
| 77 | + // Determine if we are in an external iframe in client-side. |
| 78 | + const [isExternal, setIsExternal] = useState(false) |
| 79 | + useEffect(() => { |
| 80 | + setIsExternal( |
| 81 | + isInIframe() && window.parent.location.host !== window.location.host |
| 82 | + ) |
| 83 | + }, []) |
| 84 | + |
75 | 85 | return ( |
76 | | - <form |
77 | | - className={classNames("dark:text-white", className)} |
78 | | - onSubmit={handleCheckout} |
79 | | - > |
80 | | - <header className="mb-4"> |
81 | | - <h3 className="text-b1"> |
82 | | - {checkout.recurring ? ( |
83 | | - <FormattedMessage |
84 | | - id="donate_widget.checkout.header.recurring" |
85 | | - defaultMessage="You are donating {total} every {frequency}" |
86 | | - values={{ |
87 | | - total: checkout.total.total.amount, |
88 | | - frequency: checkout.recurring.interval, |
89 | | - }} |
90 | | - /> |
91 | | - ) : ( |
92 | | - <FormattedMessage |
93 | | - id="donate_widget.checkout.header.one_time" |
94 | | - defaultMessage="You are donating {total} once" |
95 | | - values={{ |
96 | | - total: checkout.total.total.amount, |
97 | | - }} |
98 | | - /> |
| 86 | + <form className={className} onSubmit={handleCheckout}> |
| 87 | + <header className="mb-4 flex flex-col gap-2"> |
| 88 | + <div className="border dark:border-gray-1 p-4 rounded-md"> |
| 89 | + <h3 className="text-b1"> |
| 90 | + {checkout.recurring ? ( |
| 91 | + <FormattedMessage |
| 92 | + id="donate_widget.checkout.header.recurring" |
| 93 | + defaultMessage="You are donating {total} every {frequency}" |
| 94 | + values={{ |
| 95 | + total: checkout.total.total.amount, |
| 96 | + frequency: checkout.recurring.interval, |
| 97 | + }} |
| 98 | + /> |
| 99 | + ) : ( |
| 100 | + <FormattedMessage |
| 101 | + id="donate_widget.checkout.header.one_time" |
| 102 | + defaultMessage="You are donating {total} once" |
| 103 | + values={{ |
| 104 | + total: checkout.total.total.amount, |
| 105 | + }} |
| 106 | + /> |
| 107 | + )} |
| 108 | + </h3> |
| 109 | + {checkout.recurring && ( |
| 110 | + <p className="text-b4 mt-2 text-gray-1 dark:text-gray-2"> |
| 111 | + <FormattedMessage |
| 112 | + id="donate_widget.checkout.header.recurring_info" |
| 113 | + defaultMessage="You will be charged {total} today and every following {frequency}. You can cancel your recurring donation at any time." |
| 114 | + values={{ |
| 115 | + total: checkout.total.total.amount, |
| 116 | + frequency: checkout.recurring.interval, |
| 117 | + }} |
| 118 | + /> |
| 119 | + </p> |
99 | 120 | )} |
100 | | - </h3> |
| 121 | + </div> |
101 | 122 | {backUrl && ( |
102 | 123 | <Link |
103 | 124 | href={backUrl} |
104 | | - className="text-gray-1 text-b3 mt-2 flex gap-1 items-center" |
| 125 | + className="text-gray-1 dark:text-gray-2 text-b3 mt-2 flex gap-1 items-center -order-1" |
105 | 126 | > |
106 | | - <ArrowLeftIcon className="size-4" /> |
| 127 | + <ArrowLeftIcon className="size-4 fill-current" /> |
107 | 128 | <FormattedMessage |
108 | 129 | id="donate_widget.checkout.header.back" |
109 | 130 | defaultMessage="Edit your donation" |
110 | 131 | /> |
111 | 132 | </Link> |
112 | 133 | )} |
113 | 134 | </header> |
114 | | - <hr className="my-4 border-t border-gray-2" /> |
115 | | - <div className="flex max-sm:flex-col gap-4"> |
| 135 | + |
| 136 | + <div className="flex flex-col gap-4 mb-4"> |
116 | 137 | <label className="w-full"> |
117 | 138 | <FormattedMessage |
118 | 139 | id="donate_widget.checkout.email" |
@@ -142,39 +163,71 @@ export function DonateCheckout({ |
142 | 163 | </div> |
143 | 164 | </div> |
144 | 165 |
|
145 | | - <div className="mt-4"> |
146 | | - {errorMessage && ( |
147 | | - <p className="text-error text-b3 mb-2">{errorMessage}</p> |
| 166 | + {errorMessage && ( |
| 167 | + <p className="text-error text-b3 mb-2">{errorMessage}</p> |
| 168 | + )} |
| 169 | + <Button |
| 170 | + disabled={isLoading} |
| 171 | + dark |
| 172 | + className={classNames( |
| 173 | + "flex gap-2 items-center justify-center", |
| 174 | + isLoading && "text-gray-2" |
148 | 175 | )} |
149 | | - <Button |
150 | | - disabled={isLoading} |
151 | | - dark |
152 | | - className={classNames( |
153 | | - "flex gap-2 items-center justify-center", |
154 | | - isLoading && "text-gray-2" |
155 | | - )} |
156 | | - fullWidth |
157 | | - type="submit" |
158 | | - > |
159 | | - {isLoading ? ( |
160 | | - <> |
161 | | - <LoadingIcon className="motion-safe:animate-spin size-5" /> |
162 | | - <FormattedMessage |
163 | | - id="donate_widget.checkout.submitting" |
164 | | - defaultMessage="Submitting…" |
165 | | - /> |
166 | | - </> |
167 | | - ) : ( |
168 | | - <FormattedMessage |
169 | | - id="donate_widget.checkout.pay_button" |
170 | | - defaultMessage="Pay {total} now" |
171 | | - values={{ |
172 | | - total: checkout.total.total.amount, |
173 | | - }} |
174 | | - /> |
175 | | - )} |
176 | | - </Button> |
177 | | - </div> |
| 176 | + fullWidth |
| 177 | + type="submit" |
| 178 | + > |
| 179 | + <DonateCheckoutButtonText isLoading={isLoading} /> |
| 180 | + {isExternal && <ExternalLinkIcon className="fill-current" />} |
| 181 | + </Button> |
| 182 | + {isExternal && ( |
| 183 | + <p className="text-b4 text-center mt-2 text-gray-1 dark:text-gray-2"> |
| 184 | + <FormattedMessage |
| 185 | + id="donate_widget.checkout.external_notice" |
| 186 | + defaultMessage="You will be redirected to {url} for secure payment." |
| 187 | + values={{ |
| 188 | + url: typeof window !== "undefined" ? window.location.host : "", |
| 189 | + }} |
| 190 | + /> |
| 191 | + </p> |
| 192 | + )} |
178 | 193 | </form> |
179 | 194 | ) |
180 | 195 | } |
| 196 | + |
| 197 | +function DonateCheckoutButtonText({ isLoading }: { isLoading: boolean }) { |
| 198 | + const checkout = useCheckout() |
| 199 | + if (isLoading) { |
| 200 | + return ( |
| 201 | + <> |
| 202 | + <LoadingIcon className="motion-safe:animate-spin size-5" /> |
| 203 | + <FormattedMessage |
| 204 | + id="donate_widget.checkout.submitting" |
| 205 | + defaultMessage="Submitting…" |
| 206 | + /> |
| 207 | + </> |
| 208 | + ) |
| 209 | + } |
| 210 | + |
| 211 | + if (checkout.recurring) { |
| 212 | + return ( |
| 213 | + <FormattedMessage |
| 214 | + id="donate_widget.checkout.pay_button" |
| 215 | + defaultMessage="Subscribe for {total} every {frequency}" |
| 216 | + values={{ |
| 217 | + total: checkout.total.total.amount, |
| 218 | + frequency: checkout.recurring.interval, |
| 219 | + }} |
| 220 | + /> |
| 221 | + ) |
| 222 | + } |
| 223 | + |
| 224 | + return ( |
| 225 | + <FormattedMessage |
| 226 | + id="donate_widget.checkout.pay_button" |
| 227 | + defaultMessage="Pay {total} now" |
| 228 | + values={{ |
| 229 | + total: checkout.total.total.amount, |
| 230 | + }} |
| 231 | + /> |
| 232 | + ) |
| 233 | +} |
0 commit comments