From 38a74e07108138c55cad55f5428e8f150d6acabf Mon Sep 17 00:00:00 2001 From: Chris-1010 <122332721@umail.ucc.ie> Date: Sun, 23 Feb 2025 23:28:14 +0000 Subject: [PATCH] UPDATE: Have Stripe only load on pages it's needed; FIX: Pass proper prop through `ContentContext`; --- .../src/components/Checkout/CheckoutForm.tsx | 91 ++++++++++--------- frontend/src/context/ContentContext.tsx | 4 +- frontend/src/pages/CategoryPage.tsx | 5 +- frontend/src/pages/VideoPage.tsx | 37 ++++++-- 4 files changed, 80 insertions(+), 57 deletions(-) diff --git a/frontend/src/components/Checkout/CheckoutForm.tsx b/frontend/src/components/Checkout/CheckoutForm.tsx index 3415288..8fc2dfa 100644 --- a/frontend/src/components/Checkout/CheckoutForm.tsx +++ b/frontend/src/components/Checkout/CheckoutForm.tsx @@ -1,63 +1,68 @@ import React, { useState, useEffect } from "react"; -import { loadStripe } from "@stripe/stripe-js"; +import type { Stripe } from "@stripe/stripe-js"; import { EmbeddedCheckoutProvider, EmbeddedCheckout, } from "@stripe/react-stripe-js"; -import { Navigate } from "react-router-dom"; -const API_URL = import.meta.env.VITE_API_URL; +//! Unsure whether this component is used/needed in the project +// export const Return: React.FC = () => { +// const [status, setStatus] = useState(null); +// const [customerEmail, setCustomerEmail] = useState(""); -// Initialize Stripe once -const stripePromise = loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY); +// useEffect(() => { +// const queryString = window.location.search; +// const urlParams = new URLSearchParams(queryString); +// const sessionId = urlParams.get("session_id"); -export const Return: React.FC = () => { - const [status, setStatus] = useState(null); - const [customerEmail, setCustomerEmail] = useState(""); +// if (sessionId) { +// console.log("1"); +// fetch(`/api/session-status?session_id=${sessionId}`) +// .then((res) => res.json()) +// .then((data) => { +// console.log("Response Data:", data); +// setStatus(data.status); +// setCustomerEmail(data.customer_email); +// }); +// } +// }, []); - useEffect(() => { - const queryString = window.location.search; - const urlParams = new URLSearchParams(queryString); - const sessionId = urlParams.get("session_id"); +// if (status === "open") { +// return ; +// } - if (sessionId) { - console.log("1"); - fetch(`/api/session-status?session_id=${sessionId}`) - .then((res) => res.json()) - .then((data) => { - console.log("Response Data:", data); - setStatus(data.status); - setCustomerEmail(data.customer_email); - }); - } - }, []); +// if (status === "complete") { +// return ( +//
+//

+// We appreciate your business! A confirmation email will be sent to{" "} +// {customerEmail}. If you have any questions, please email{" "} +// orders@example.com. +//

+//
+// ); +// } - if (status === "open") { - return ; - } +// return null; +// }; - if (status === "complete") { - return ( -
-

- We appreciate your business! A confirmation email will be sent to{" "} - {customerEmail}. If you have any questions, please email{" "} - orders@example.com. -

-
- ); - } - - return null; -}; - -// Main CheckoutForm component interface CheckoutFormProps { streamerID: number; onClose: () => void; } -const CheckoutForm: React.FC = ({ onClose, streamerID }) => { +const CheckoutForm: React.FC = ({ streamerID, onClose }) => { + const [stripePromise, setStripePromise] = + useState | null>(null); + + useEffect(() => { + const initializeStripe = async () => { + const { loadStripe } = await import("@stripe/stripe-js"); + setStripePromise(loadStripe(import.meta.env.VITE_STRIPE_PUBLISHABLE_KEY)); + }; + initializeStripe(); + }, []); + const fetchClientSecret = () => { return fetch(`/api/create-checkout-session?streamer_id=${streamerID}`, { method: "POST", diff --git a/frontend/src/context/ContentContext.tsx b/frontend/src/context/ContentContext.tsx index ab1bf6f..4e475e8 100644 --- a/frontend/src/context/ContentContext.tsx +++ b/frontend/src/context/ContentContext.tsx @@ -11,7 +11,7 @@ interface Item { interface StreamItem extends Item { type: "stream"; - streamer: string; + username: string; streamCategory: string; } @@ -56,7 +56,7 @@ export function ContentProvider({ children }: { children: React.ReactNode }) { type: "stream", id: stream.user_id, title: stream.title, - streamer: stream.username, + username: stream.username, streamCategory: stream.category_name, viewers: stream.num_viewers, thumbnail: diff --git a/frontend/src/pages/CategoryPage.tsx b/frontend/src/pages/CategoryPage.tsx index 1c2e745..504462f 100644 --- a/frontend/src/pages/CategoryPage.tsx +++ b/frontend/src/pages/CategoryPage.tsx @@ -49,7 +49,7 @@ const CategoryPage: React.FC = () => { setStreamOffset((prev) => prev + data.length); - const processedStreams = data.map((stream: any) => ({ + const processedStreams: StreamData[] = data.map((stream: any) => ({ type: "stream", id: stream.user_id, title: stream.title, @@ -80,9 +80,10 @@ const CategoryPage: React.FC = () => { const logOnScroll = async () => { if (hasMoreData && listRowRef.current) { const newCategories = await fetchCategoryStreams(); - if (newCategories?.length > 0) { + if (newCategories && newCategories.length > 0) { listRowRef.current.addMoreItems(newCategories); } + else console.log("No more data to fetch"); } }; diff --git a/frontend/src/pages/VideoPage.tsx b/frontend/src/pages/VideoPage.tsx index 01c2575..dbbbcb5 100644 --- a/frontend/src/pages/VideoPage.tsx +++ b/frontend/src/pages/VideoPage.tsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, lazy, Suspense } from "react"; import { ToggleButton } from "../components/Input/Button"; import ChatPanel from "../components/Stream/ChatPanel"; import { useNavigate, useParams } from "react-router-dom"; @@ -8,10 +8,12 @@ import { useFollow } from "../hooks/useFollow"; import VideoPlayer from "../components/Stream/VideoPlayer"; import { SocketProvider } from "../context/SocketContext"; import AuthModal from "../components/Auth/AuthModal"; -import CheckoutForm, { Return } from "../components/Checkout/CheckoutForm"; import DynamicPageContent from "../components/Layout/DynamicPageContent"; import { useSidebar } from "../context/SidebarContext"; +// Lazy load the CheckoutForm component +const CheckoutForm = lazy(() => import("../components/Checkout/CheckoutForm")); + interface VideoPageProps { streamerId: number; } @@ -33,6 +35,7 @@ const VideoPage: React.FC = ({ streamerId }) => { const { isFollowing, checkFollowStatus, followUser, unfollowUser } = useFollow(); const { showAuthModal, setShowAuthModal } = useAuthModal(); + const [isStripeReady, setIsStripeReady] = useState(false); const [showCheckout, setShowCheckout] = useState(false); const showReturn = window.location.search.includes("session_id"); const navigate = useNavigate(); @@ -107,6 +110,15 @@ const VideoPage: React.FC = ({ streamerId }) => { }; }, []); + // Load Stripe in the background when component mounts + useEffect(() => { + const loadStripe = async () => { + await import("@stripe/stripe-js"); + setIsStripeReady(true); + }; + loadStripe(); + }, []); + const toggleChat = () => { setIsChatOpen((prev) => !prev); }; @@ -235,26 +247,31 @@ const VideoPage: React.FC = ({ streamerId }) => { {/* Subscribe Button */}
{showCheckout && ( - setShowCheckout(false)} - streamerID={streamerId} - /> + Loading checkout...}> + setShowCheckout(false)} + streamerID={streamerId} + /> + )} - {showReturn && } + {/* {showReturn && } */} {showAuthModal && ( setShowAuthModal(false)} /> )}