layout.tsx 5.4 KB

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