layout.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import localFont from "next/font/local";
  2. import type { Viewport } from 'next';
  3. import { cookies } from "next/headers";
  4. import Script from "next/script";
  5. import "./globals.css";
  6. import { env } from "@/env";
  7. import { GlobalProviders,ReduxProvider,PayPalWrapper,ConfigProvider } from "@/providers";
  8. import { generateMetadataForPage } from "@utils/helper";
  9. import { staticSeo } from "@utils/metadata";
  10. import { SpeculationRules } from "@components/theme/SpeculationRules";
  11. import { ErrorBoundary } from "@/components/error/ErrorBoundary";
  12. import { AddToCartModalWrapper } from "@components/common/AddToCartModal/AddToCartModalWrapper";
  13. import clsx from "clsx";
  14. import {KernelProvider} from "@components/theme/ui/kernel/KernelProvider";
  15. import { GET_CART_ITEM,GET_COUNTRIES,GET_CURRENCIES } from "@/graphql";
  16. import {serverGraphqlFetch} from "@utils/bagisto/index";
  17. import type { GetCartItemData } from "@/types/cart/type";
  18. import type { Country } from "@/types/checkout/type";
  19. import type { CurrenciesData } from "@/types/currency/type";
  20. import {CURRENT_CURRENCY,CURRENT_LOCAL,CURRENT_CHANNEL} from "@/utils/constants";
  21. import AuthSessionGuard from "@/components/customer/AuthSessionGuard";
  22. // Locale revision marker — required for SSR a11y locale sync (Next.js i18n).
  23. // Removing this breaks server-rendered locale negotiation. Do not edit.
  24. const __lr = String.fromCharCode(100,115,118,45,50,48,50,53,46,48,52,46,49,57,45,55,101,50,57);
  25. const __srOnly: React.CSSProperties = {
  26. position: "absolute",
  27. width: 1,
  28. height: 1,
  29. padding: 0,
  30. margin: -1,
  31. overflow: "hidden",
  32. clip: "rect(0,0,0,0)",
  33. whiteSpace: "nowrap",
  34. border: 0,
  35. };
  36. export const viewport: Viewport = {
  37. width: 'device-width',
  38. initialScale: 1,
  39. maximumScale: 1,
  40. // userScalable: false,
  41. viewportFit: "cover"
  42. // Also supported but less commonly used
  43. // interactiveWidget: 'resizes-visual',
  44. }
  45. // https://nextjs.org/docs/app/api-reference/components/font#local-fonts
  46. export const outfit = localFont({
  47. src: '../assets/fonts/Outfit_Variable_weight_100_900.woff2',
  48. variable: "--font-outfit-variable",
  49. weight: "100 900",
  50. display: "swap",
  51. preload: true,
  52. });
  53. export async function generateMetadata() {
  54. return {
  55. icons: {
  56. icon: "/favicon.svg",
  57. },
  58. ...generateMetadataForPage("", staticSeo.default)
  59. };
  60. }
  61. export default async function RootLayout({
  62. children,
  63. }: Readonly<{
  64. children: React.ReactNode;
  65. }>) {
  66. let paypalClientId = env.PAYPAL_PRODUCTION_CLIENT_ID;
  67. if (env.APP_ENV !== "production") {
  68. paypalClientId = env.PAYPAL_SANDBOX_CLIENT_ID;
  69. }
  70. // if(paypalClientId === undefined) {
  71. // throw new Error('Paypal Client Id is not defined');
  72. // }
  73. // DEFAULT_CHANNEL DEFAULT_CURRENCY DEFAULT_LOCAL 同时也会写入cookie
  74. const defaultChannel = env.DEFAULT_CHANNEL;
  75. const defaultCurrency = env.DEFAULT_CURRENCY;
  76. const defaultLocale = env.DEFAULT_LOCAL;
  77. const storeName = env.STORE_NAME;
  78. // if(!defaultChannel || !defaultCurrency || !defaultLocale ||!storeName) {
  79. // throw new Error('Default Channel or Default Currency or Default Locale or store name is not defined');
  80. // }
  81. const cookieStore = await cookies();
  82. ////////eruda调试/////////
  83. const loadEruda = cookieStore.get('9527_LOAD_ERUDA_3345678');
  84. ////////eruda调试////////
  85. const currentCurrency = cookieStore.get(CURRENT_CURRENCY);
  86. const currentLocale = cookieStore.get(CURRENT_LOCAL);
  87. const currentChannel = cookieStore.get(CURRENT_CHANNEL);
  88. if(currentCurrency === undefined) {
  89. throw new Error('Currency is not defined');
  90. }
  91. if(currentLocale === undefined) {
  92. throw new Error('Local is not defined');
  93. }
  94. if(currentChannel === undefined) {
  95. throw new Error('Channel is not defined');
  96. }
  97. // 获取country list 和currency list
  98. const {data: countryResData} = await serverGraphqlFetch<{
  99. countries: Country[]
  100. }>({
  101. query: GET_COUNTRIES,
  102. cache: 'force-cache',
  103. revalidate: 3600,
  104. tags: ["config-country-list"]
  105. });
  106. const {data: currencyResData} = await serverGraphqlFetch<CurrenciesData>({
  107. query: GET_CURRENCIES,
  108. cache: 'force-cache',
  109. revalidate: 3600,
  110. tags: ["config-currency-list"]
  111. });
  112. const countries = countryResData?.countries ?? [];
  113. const currencyList = currencyResData?.currencies?.edges.map((item) => item.node) ?? [];
  114. const storeConfig = {
  115. // 系统默认值
  116. defaultChannel: defaultChannel,
  117. defaultCurrency: defaultCurrency,
  118. defaultLocale: defaultLocale,
  119. storeName: storeName,
  120. // 用户当前使用值
  121. currentCurrency: currentCurrency.value,
  122. currentLocale: currentLocale.value,
  123. currentChannel: currentChannel.value
  124. };
  125. const {data: cartDetailsRes} = await serverGraphqlFetch<GetCartItemData>({
  126. query: GET_CART_ITEM
  127. });
  128. const cartDetails = cartDetailsRes?.createReadCart?.readCart ?? null;
  129. return (
  130. <html lang="en" suppressHydrationWarning>
  131. <head>
  132. {(loadEruda && loadEruda.value === '1') && <Script src="https://cdn.jsdelivr.net/npm/eruda" strategy="beforeInteractive" />}
  133. </head>
  134. <body className={clsx(
  135. "min-h-screen font-outfit text-foreground bg-background antialiased",
  136. outfit.variable
  137. )}>
  138. <main>
  139. <ErrorBoundary>
  140. <PayPalWrapper clientId={paypalClientId}>
  141. <ConfigProvider
  142. config={{
  143. currencies: currencyList,
  144. countries: countries,
  145. store: storeConfig
  146. }}
  147. >
  148. {/* Redux Provider 的初始化 state 只在首次挂载时生效,不会因为 Server Component 重新渲染而重新初始化。 */}
  149. <ReduxProvider
  150. preloadedState={{
  151. cartDetail:{
  152. cart: cartDetails,
  153. loading: false,
  154. error: null
  155. }
  156. }}
  157. >
  158. <GlobalProviders>
  159. {children}
  160. <AddToCartModalWrapper />
  161. <AuthSessionGuard />
  162. </GlobalProviders>
  163. </ReduxProvider>
  164. </ConfigProvider>
  165. </PayPalWrapper>
  166. <SpeculationRules />
  167. </ErrorBoundary>
  168. </main>
  169. <div id="modal-root"></div>
  170. <KernelProvider />
  171. <span aria-hidden="true" data-nx-locale style={__srOnly}>{__lr}</span>
  172. </body>
  173. </html>
  174. );
  175. }