From f62e434dff863efab8e27fbc312e8f41ac0b0eb5 Mon Sep 17 00:00:00 2001 From: Tony Grosinger Date: Thu, 1 Feb 2024 19:55:11 -0800 Subject: [PATCH] Fix club membership form in dark mode --- src/app/club/page.tsx | 11 +++++- src/app/club/payment.tsx | 85 +++++++++++++++++++++++++++++++--------- src/styles/tailwind.css | 12 ++++++ 3 files changed, 89 insertions(+), 19 deletions(-) diff --git a/src/app/club/page.tsx b/src/app/club/page.tsx index 15580ae..dbdc54c 100644 --- a/src/app/club/page.tsx +++ b/src/app/club/page.tsx @@ -2,12 +2,12 @@ import { type Metadata } from 'next' import Image from 'next/image' import Link from 'next/link' +import dynamic from "next/dynamic"; import clsx from 'clsx' import { Container } from '@/components/Container' import { UserPlusIcon, GiftIcon, EnvelopeIcon, UserGroupIcon } from '@heroicons/react/24/solid' import interiorEmptyImage from '@/images/photos/interior-empty.jpg' -import { ClubPayment } from './payment'; function SocialLink({ className, @@ -43,6 +43,15 @@ export const metadata: Metadata = { // TODO: Replace interiorEmptyImage with a photo from a potluck export default function Club() { + + // Dynamic import since ClubPayment uses `document` + const ClubPayment = dynamic( + () => { + return import("./payment"); + }, + { ssr: false } + ); + return (
diff --git a/src/app/club/payment.tsx b/src/app/club/payment.tsx index db2805b..a1df7b9 100644 --- a/src/app/club/payment.tsx +++ b/src/app/club/payment.tsx @@ -1,6 +1,6 @@ "use client" -import React, { useEffect, useState, FormEvent } from 'react'; +import React, { useEffect, useState, FormEvent, useCallback } from 'react'; import { AddressElement, Elements } from '@stripe/react-stripe-js'; import { Appearance, StripeAddressElementOptions, StripeElementsOptions, loadStripe } from '@stripe/stripe-js'; import { @@ -166,7 +166,7 @@ function CheckoutForm({ {/* Membership Type */} - + Select a membership type @@ -177,8 +177,8 @@ function CheckoutForm({ value={membership} className={({ active }) => classNames( - active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-300', - 'relative flex cursor-pointer rounded-lg border bg-white p-4 shadow-sm focus:outline-none' + active ? 'border-indigo-600 ring-2 ring-indigo-600' : 'border-gray-200 dark:border-gray-500', + 'relative flex cursor-pointer rounded-lg border bg-white hover:bg-gray-50 hover:dark:bg-zinc-600 dark:bg-zinc-700 p-4 shadow-sm focus:outline-none' ) } > @@ -186,7 +186,7 @@ function CheckoutForm({ <> - + {membership.title} @@ -194,7 +194,7 @@ function CheckoutForm({ - ${membership.price} + ${membership.price} /yr @@ -215,7 +215,7 @@ function CheckoutForm({ {/* Additional donation */} - + Additional donation
@@ -228,9 +228,9 @@ function CheckoutForm({ 'cursor-pointer focus:outline-none', option === -1 ? 'col-span-2' : '', checked - ? 'ring-2 ring-indigo-600 ring-offset-2' - : 'ring-1 ring-inset ring-gray-300 bg-white text-gray-900 hover:bg-gray-50', - 'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1' + ? 'ring-2 ring-indigo-600' + : 'ring-1 ring-inset ring-gray-200 dark:ring-gray-500 text-gray-900 dark:text-white hover:bg-gray-50 hover:dark:bg-zinc-600', + 'flex items-center justify-center rounded-md py-3 px-3 text-sm font-semibold sm:flex-1 bg-white dark:bg-zinc-700' ) } > @@ -250,7 +250,7 @@ function CheckoutForm({ min="0" step="1" onChange={(e) => setCustomAmount(e.target.value)} - className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-500 sm:text-sm sm:leading-6" + className="block w-full rounded-md border-0 py-1.5 pl-7 pr-12 text-gray-900 dark:text-white dark:bg-zinc-700 ring-1 ring-inset ring-gray-200 dark:ring-gray-500 placeholder:text-gray-400 focus:ring-1 focus:ring-inset focus:ring-gray-500 sm:text-sm sm:leading-6" placeholder="Custom" aria-describedby="price-currency" /> @@ -274,12 +274,12 @@ function CheckoutForm({
-

+

About you

-
-
-

+

Payment

@@ -329,9 +329,37 @@ function CheckoutForm({ ); } -export function ClubPayment() { +const DEFAULT_OPTIONS = { + config: { attributes: true, childList: true, subtree: true }, +}; + +function useMutationObservable(targetEl: Node, cb: MutationCallback, options = DEFAULT_OPTIONS) { + const [observer, setObserver] = useState(null); + + useEffect(() => { + const obs = new MutationObserver(cb); + setObserver(obs); + }, [cb, options, setObserver]); + + useEffect(() => { + if (!observer) return; + const { config } = options; + observer.observe(targetEl, config); + return () => { + if (observer) { + observer.disconnect(); + } + }; + }, [observer, targetEl, options]); +} + +export default function ClubPayment() { const [clientSecret, setClientSecret] = useState(''); const [paymentIntent, setPaymentIntent] = useState(''); + + const htmlEl = document.getElementsByTagName('html')[0]; + const darkTheme = htmlEl.classList.contains("dark"); + useEffect(() => { // Create PaymentIntent as soon as the page loads using our local API fetch('api/stripe_intent', { @@ -349,15 +377,36 @@ export function ClubPayment() { }); }, []); + const styles = getComputedStyle(htmlEl); + const appearance: Appearance = { theme: 'stripe', + variables: { + colorBackground: styles.getPropertyValue('--stripe-background'), + colorText: styles.getPropertyValue('--stripe-foreground'), + }, labels: 'floating', }; + const cb = useCallback( + () => { + const updatedHtmlEl = document.getElementsByTagName('html')[0]; + const updatedDarkTheme = updatedHtmlEl.classList.contains("dark"); + + if (updatedDarkTheme !== darkTheme) { + location.reload(); + } + }, + [darkTheme] + ) + + useMutationObservable(htmlEl, cb); + const options: StripeElementsOptions = { clientSecret, - appearance + appearance, } + return <> {clientSecret && (