layout.tsx 5.7 KB

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