fix(subscription): remove animations affecting stripe dialog
This commit is contained in:
@@ -2,26 +2,17 @@
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
z-index: 70;
|
z-index: 70;
|
||||||
display: block;
|
display: flex;
|
||||||
height: 100dvh;
|
align-items: center;
|
||||||
padding:
|
justify-content: center;
|
||||||
max(24px, env(safe-area-inset-top))
|
padding: 20px;
|
||||||
20px
|
|
||||||
max(20px, env(safe-area-inset-bottom));
|
|
||||||
overflow-y: auto;
|
|
||||||
background: rgba(0, 0, 0, 0.5);
|
background: rgba(0, 0, 0, 0.5);
|
||||||
overscroll-behavior: contain;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dialog {
|
.dialog {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 420px;
|
max-width: 420px;
|
||||||
max-height: calc(
|
max-height: calc(100vh - 40px);
|
||||||
100dvh
|
|
||||||
- max(48px, env(safe-area-inset-top))
|
|
||||||
- max(20px, env(safe-area-inset-bottom))
|
|
||||||
);
|
|
||||||
margin: clamp(0px, 6dvh, 48px) auto 0;
|
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
border-radius: 32px;
|
border-radius: 32px;
|
||||||
background: var(--color-page-background, #ffffff);
|
background: var(--color-page-background, #ffffff);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import {
|
|||||||
} from "@/stores/payment/payment-context";
|
} from "@/stores/payment/payment-context";
|
||||||
import { AppEnvUtil, Logger, Result } from "@/utils";
|
import { AppEnvUtil, Logger, Result } from "@/utils";
|
||||||
|
|
||||||
|
import { StripePaymentDialog } from "./stripe-payment-dialog";
|
||||||
import dialogStyles from "./stripe-payment-dialog.module.css";
|
import dialogStyles from "./stripe-payment-dialog.module.css";
|
||||||
import { SubscriptionCtaButton } from "./subscription-cta-button";
|
import { SubscriptionCtaButton } from "./subscription-cta-button";
|
||||||
import styles from "./subscription-cta-button.module.css";
|
import styles from "./subscription-cta-button.module.css";
|
||||||
@@ -34,6 +35,9 @@ export function SubscriptionCheckoutButton({
|
|||||||
const payment = usePaymentState();
|
const payment = usePaymentState();
|
||||||
const paymentDispatch = usePaymentDispatch();
|
const paymentDispatch = usePaymentDispatch();
|
||||||
const launchedNonceRef = useRef(0);
|
const launchedNonceRef = useRef(0);
|
||||||
|
const [hiddenStripeClientSecret, setHiddenStripeClientSecret] = useState<
|
||||||
|
string | null
|
||||||
|
>(null);
|
||||||
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
const [isConfirmingEzpay, setIsConfirmingEzpay] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -94,7 +98,7 @@ export function SubscriptionCheckoutButton({
|
|||||||
payment.isCreatingOrder ||
|
payment.isCreatingOrder ||
|
||||||
payment.isPollingOrder;
|
payment.isPollingOrder;
|
||||||
const readyLabel =
|
const readyLabel =
|
||||||
subscriptionType === "topup" ? "Pay and Top Up" : "Pay and Activate";
|
subscriptionType === "topup" ? "Pay and Top Up" : "Pay and Activiate";
|
||||||
const agreementLabel =
|
const agreementLabel =
|
||||||
subscriptionType === "topup"
|
subscriptionType === "topup"
|
||||||
? "Confirm the agreement and Top Up"
|
? "Confirm the agreement and Top Up"
|
||||||
@@ -110,9 +114,32 @@ export function SubscriptionCheckoutButton({
|
|||||||
const handleClick = () => {
|
const handleClick = () => {
|
||||||
if (disabled || isLoading) return;
|
if (disabled || isLoading) return;
|
||||||
setIsConfirmingEzpay(false);
|
setIsConfirmingEzpay(false);
|
||||||
|
setHiddenStripeClientSecret(null);
|
||||||
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
paymentDispatch({ type: "PaymentCreateOrderSubmitted" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleStripeClose = () => {
|
||||||
|
if (stripeClientSecret) {
|
||||||
|
setHiddenStripeClientSecret(stripeClientSecret);
|
||||||
|
}
|
||||||
|
if (payment.orderStatus !== "paid") {
|
||||||
|
paymentDispatch({ type: "PaymentReset" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleStripeConfirmed = () => {
|
||||||
|
if (stripeClientSecret) {
|
||||||
|
setHiddenStripeClientSecret(stripeClientSecret);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const stripeClientSecret = payment.payParams
|
||||||
|
? getStripeClientSecret(payment.payParams)
|
||||||
|
: null;
|
||||||
|
const shouldShowStripeDialog =
|
||||||
|
stripeClientSecret !== null &&
|
||||||
|
stripeClientSecret !== hiddenStripeClientSecret &&
|
||||||
|
!payment.isPaid;
|
||||||
const ezpayPaymentUrl = payment.payParams
|
const ezpayPaymentUrl = payment.payParams
|
||||||
? getPaymentUrl(payment.payParams)
|
? getPaymentUrl(payment.payParams)
|
||||||
: null;
|
: null;
|
||||||
@@ -181,6 +208,13 @@ export function SubscriptionCheckoutButton({
|
|||||||
onConfirm={handleEzpayConfirm}
|
onConfirm={handleEzpayConfirm}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
{shouldShowStripeDialog ? (
|
||||||
|
<StripePaymentDialog
|
||||||
|
clientSecret={stripeClientSecret}
|
||||||
|
onClose={handleStripeClose}
|
||||||
|
onConfirmed={handleStripeConfirmed}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -332,7 +366,7 @@ function EzpayRedirectConfirmDialog({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getStripeClientSecret(
|
function getStripeClientSecret(
|
||||||
payParams: Record<string, unknown>,
|
payParams: Record<string, unknown>,
|
||||||
): string | null {
|
): string | null {
|
||||||
const provider = payParams.provider;
|
const provider = payParams.provider;
|
||||||
|
|||||||
@@ -6,7 +6,6 @@
|
|||||||
radial-gradient(circle at 8% 4%, rgba(255, 255, 255, 0.95) 0 90px, transparent 160px),
|
radial-gradient(circle at 8% 4%, rgba(255, 255, 255, 0.95) 0 90px, transparent 160px),
|
||||||
linear-gradient(180deg, #fff9fb 0%, #fcf3f4 52%, #fffefe 100%);
|
linear-gradient(180deg, #fff9fb 0%, #fcf3f4 52%, #fffefe 100%);
|
||||||
padding: 18px 20px 10px;
|
padding: 18px 20px 10px;
|
||||||
animation: pageEnter 360ms ease-out both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header {
|
.header {
|
||||||
@@ -24,7 +23,6 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 18px;
|
gap: 18px;
|
||||||
margin-top: 14px;
|
margin-top: 14px;
|
||||||
animation: stackEnter 420ms ease-out 80ms both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.userSlot {
|
.userSlot {
|
||||||
@@ -70,12 +68,10 @@
|
|||||||
|
|
||||||
.paymentMethodSlot {
|
.paymentMethodSlot {
|
||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
animation: stackEnter 420ms ease-out 150ms both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ctaSlot {
|
.ctaSlot {
|
||||||
margin-top: 22px;
|
margin-top: 22px;
|
||||||
animation: stackEnter 420ms ease-out 190ms both;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.agreementSlot {
|
.agreementSlot {
|
||||||
@@ -96,27 +92,3 @@
|
|||||||
.agreementLink:hover {
|
.agreementLink:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes pageEnter {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(8px);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes stackEnter {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(14px);
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,14 +13,12 @@ import { useUserDispatch } from "@/stores/user/user-context";
|
|||||||
import { ROUTES } from "@/router/routes";
|
import { ROUTES } from "@/router/routes";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getStripeClientSecret,
|
|
||||||
SubscriptionBackLink,
|
SubscriptionBackLink,
|
||||||
SubscriptionCheckoutButton,
|
SubscriptionCheckoutButton,
|
||||||
SubscriptionCoinsOfferSection,
|
SubscriptionCoinsOfferSection,
|
||||||
SubscriptionPaymentMethod,
|
SubscriptionPaymentMethod,
|
||||||
SubscriptionPaymentSuccessDialog,
|
SubscriptionPaymentSuccessDialog,
|
||||||
SubscriptionVipOfferSection,
|
SubscriptionVipOfferSection,
|
||||||
StripePaymentDialog,
|
|
||||||
} from "./components";
|
} from "./components";
|
||||||
import styles from "./components/subscription-screen.module.css";
|
import styles from "./components/subscription-screen.module.css";
|
||||||
import {
|
import {
|
||||||
@@ -51,9 +49,6 @@ export function SubscriptionScreen({
|
|||||||
const refreshedPaidOrderRef = useRef<string | null>(null);
|
const refreshedPaidOrderRef = useRef<string | null>(null);
|
||||||
const resumedPendingOrderRef = useRef<string | null>(null);
|
const resumedPendingOrderRef = useRef<string | null>(null);
|
||||||
const successDialogShownOrderRef = useRef<string | null>(null);
|
const successDialogShownOrderRef = useRef<string | null>(null);
|
||||||
const [hiddenStripeDialogKey, setHiddenStripeDialogKey] = useState<
|
|
||||||
string | null
|
|
||||||
>(null);
|
|
||||||
const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] =
|
const [showPaymentSuccessDialog, setShowPaymentSuccessDialog] =
|
||||||
useState(false);
|
useState(false);
|
||||||
const canSubscribeVip = subscriptionType === "vip";
|
const canSubscribeVip = subscriptionType === "vip";
|
||||||
@@ -161,16 +156,6 @@ export function SubscriptionScreen({
|
|||||||
payment.isPollingOrder;
|
payment.isPollingOrder;
|
||||||
const canActivate =
|
const canActivate =
|
||||||
selectedPlan !== null && payment.agreed && !isPaymentBusy;
|
selectedPlan !== null && payment.agreed && !isPaymentBusy;
|
||||||
const stripeClientSecret = payment.payParams
|
|
||||||
? getStripeClientSecret(payment.payParams)
|
|
||||||
: null;
|
|
||||||
const stripeDialogKey = stripeClientSecret
|
|
||||||
? `${payment.launchNonce}:${stripeClientSecret}`
|
|
||||||
: null;
|
|
||||||
const shouldShowStripeDialog =
|
|
||||||
stripeClientSecret !== null &&
|
|
||||||
stripeDialogKey !== hiddenStripeDialogKey &&
|
|
||||||
!payment.isPaid;
|
|
||||||
|
|
||||||
const finishPaymentSuccessClose = () => {
|
const finishPaymentSuccessClose = () => {
|
||||||
setShowPaymentSuccessDialog(false);
|
setShowPaymentSuccessDialog(false);
|
||||||
@@ -184,21 +169,6 @@ export function SubscriptionScreen({
|
|||||||
finishPaymentSuccessClose();
|
finishPaymentSuccessClose();
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleStripeClose = () => {
|
|
||||||
if (stripeDialogKey) {
|
|
||||||
setHiddenStripeDialogKey(stripeDialogKey);
|
|
||||||
}
|
|
||||||
if (payment.orderStatus !== "paid") {
|
|
||||||
paymentDispatch({ type: "PaymentReset" });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleStripeConfirmed = () => {
|
|
||||||
if (stripeDialogKey) {
|
|
||||||
setHiddenStripeDialogKey(stripeDialogKey);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (canSubscribeVip) {
|
if (canSubscribeVip) {
|
||||||
const firstVipPlanId = vipOfferPlans[0]?.id ?? "";
|
const firstVipPlanId = vipOfferPlans[0]?.id ?? "";
|
||||||
@@ -242,28 +212,16 @@ export function SubscriptionScreen({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<MobileShell>
|
||||||
<MobileShell>
|
<div className={styles.shell}>
|
||||||
<div className={styles.shell}>
|
<header className={styles.header}>
|
||||||
<header className={styles.header}>
|
<SubscriptionBackLink className={styles.backSlot} />
|
||||||
<SubscriptionBackLink className={styles.backSlot} />
|
</header>
|
||||||
</header>
|
|
||||||
|
|
||||||
<div className={styles.offerStack}>
|
<div className={styles.offerStack}>
|
||||||
{canSubscribeVip ? (
|
{canSubscribeVip ? (
|
||||||
<SubscriptionVipOfferSection
|
<SubscriptionVipOfferSection
|
||||||
plans={vipOfferPlans}
|
plans={vipOfferPlans}
|
||||||
selectedPlanId={payment.selectedPlanId || selectedPlan?.id || null}
|
|
||||||
onSelectPlan={(planId) =>
|
|
||||||
paymentDispatch({
|
|
||||||
type: "PaymentPlanSelected",
|
|
||||||
planId,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
<SubscriptionCoinsOfferSection
|
|
||||||
plans={directCoinsPlans}
|
|
||||||
selectedPlanId={payment.selectedPlanId || selectedPlan?.id || null}
|
selectedPlanId={payment.selectedPlanId || selectedPlan?.id || null}
|
||||||
onSelectPlan={(planId) =>
|
onSelectPlan={(planId) =>
|
||||||
paymentDispatch({
|
paymentDispatch({
|
||||||
@@ -272,78 +230,80 @@ export function SubscriptionScreen({
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
) : null}
|
||||||
|
<SubscriptionCoinsOfferSection
|
||||||
<section className={styles.paymentMethodSlot}>
|
plans={directCoinsPlans}
|
||||||
<SubscriptionPaymentMethod
|
selectedPlanId={payment.selectedPlanId || selectedPlan?.id || null}
|
||||||
value={payment.payChannel}
|
onSelectPlan={(planId) =>
|
||||||
disabled={isPaymentBusy}
|
paymentDispatch({
|
||||||
onChange={(payChannel) =>
|
type: "PaymentPlanSelected",
|
||||||
paymentDispatch({
|
planId,
|
||||||
type: "PaymentPayChannelChanged",
|
})
|
||||||
payChannel,
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<div className={styles.ctaSlot}>
|
|
||||||
<SubscriptionCheckoutButton
|
|
||||||
disabled={!canActivate}
|
|
||||||
subscriptionType={subscriptionType}
|
|
||||||
returnTo={returnTo}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className={styles.agreementSlot}>
|
|
||||||
<Checkbox
|
|
||||||
checked={payment.agreed}
|
|
||||||
onChange={(agreed) =>
|
|
||||||
paymentDispatch({
|
|
||||||
type: "PaymentAgreementChanged",
|
|
||||||
agreed,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
label={
|
|
||||||
<span className={styles.agreementLabel}>
|
|
||||||
I agree to the{" "}
|
|
||||||
<a
|
|
||||||
href={AppConstants.privacyPolicyUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.agreementLink}
|
|
||||||
>
|
|
||||||
VIP Membership Benefits Agreement
|
|
||||||
</a>{" "}
|
|
||||||
and{" "}
|
|
||||||
<a
|
|
||||||
href={AppConstants.termsOfServiceUrl}
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
className={styles.agreementLink}
|
|
||||||
>
|
|
||||||
Automatic renewal Agreement
|
|
||||||
</a>
|
|
||||||
</span>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<SubscriptionPaymentSuccessDialog
|
|
||||||
open={showPaymentSuccessDialog}
|
|
||||||
subscriptionType={subscriptionType}
|
|
||||||
onClose={handlePaymentSuccessClose}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</MobileShell>
|
|
||||||
|
|
||||||
{shouldShowStripeDialog ? (
|
<section className={styles.paymentMethodSlot}>
|
||||||
<StripePaymentDialog
|
<SubscriptionPaymentMethod
|
||||||
clientSecret={stripeClientSecret}
|
value={payment.payChannel}
|
||||||
onClose={handleStripeClose}
|
disabled={isPaymentBusy}
|
||||||
onConfirmed={handleStripeConfirmed}
|
onChange={(payChannel) =>
|
||||||
|
paymentDispatch({
|
||||||
|
type: "PaymentPayChannelChanged",
|
||||||
|
payChannel,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div className={styles.ctaSlot}>
|
||||||
|
<SubscriptionCheckoutButton
|
||||||
|
disabled={!canActivate}
|
||||||
|
subscriptionType={subscriptionType}
|
||||||
|
returnTo={returnTo}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={styles.agreementSlot}>
|
||||||
|
<Checkbox
|
||||||
|
checked={payment.agreed}
|
||||||
|
onChange={(agreed) =>
|
||||||
|
paymentDispatch({
|
||||||
|
type: "PaymentAgreementChanged",
|
||||||
|
agreed,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<span className={styles.agreementLabel}>
|
||||||
|
I agree to the{" "}
|
||||||
|
<a
|
||||||
|
href={AppConstants.privacyPolicyUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.agreementLink}
|
||||||
|
>
|
||||||
|
VIP Membership Benefits Agreement
|
||||||
|
</a>{" "}
|
||||||
|
and{" "}
|
||||||
|
<a
|
||||||
|
href={AppConstants.termsOfServiceUrl}
|
||||||
|
target="_blank"
|
||||||
|
rel="noreferrer"
|
||||||
|
className={styles.agreementLink}
|
||||||
|
>
|
||||||
|
Automatic renewal Agreement
|
||||||
|
</a>
|
||||||
|
</span>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<SubscriptionPaymentSuccessDialog
|
||||||
|
open={showPaymentSuccessDialog}
|
||||||
|
subscriptionType={subscriptionType}
|
||||||
|
onClose={handlePaymentSuccessClose}
|
||||||
/>
|
/>
|
||||||
) : null}
|
</div>
|
||||||
</>
|
</MobileShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user