_app.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { SWRConfig } from "swr";
  2. import Script from "next/script";
  3. import type { NextPage } from "next";
  4. import { useRouter } from "next/router";
  5. import type { AppProps } from "next/app";
  6. import App, { AppContext } from "next/app";
  7. import { ReactElement, ReactNode, useEffect, useMemo } from "react";
  8. import { get } from "libs/http";
  9. import { pageview } from "libs/gtag";
  10. import { Context } from "libs/context";
  11. import { GA_TRACKING_ID } from "libs/config";
  12. import Layout from "components/common/Layout";
  13. import getSiteConfig, { SiteConfig } from "libs/getSiteConfig";
  14. import "styles/globals.scss";
  15. import { SeoHead, SeoHeadConfig } from "components/SeoHead";
  16. export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
  17. getLayout?: (page: ReactElement) => ReactNode;
  18. };
  19. type AppPropsWithLayout = AppProps & {
  20. Component: NextPageWithLayout;
  21. pageProps: {
  22. fallback?: Docs;
  23. siteConfig: SiteConfig;
  24. };
  25. };
  26. const MyApp = ({ Component, pageProps }: AppPropsWithLayout) => {
  27. const { fallback, genre, siteConfig, ...otherProps } = pageProps;
  28. const router = useRouter();
  29. const seoConfig: SeoHeadConfig = useMemo(() => {
  30. return {
  31. title: siteConfig.title,
  32. description: siteConfig.description,
  33. keywords: siteConfig.keywords,
  34. url: `https://${siteConfig.host}`,
  35. canonical: `https://${siteConfig.host}`,
  36. jsonLd: JSON.stringify(siteConfig.jsonLd),
  37. siteName: siteConfig.siteName,
  38. img: siteConfig.touchIcon,
  39. };
  40. }, [
  41. siteConfig.description,
  42. siteConfig.host,
  43. siteConfig.jsonLd,
  44. siteConfig.keywords,
  45. siteConfig.siteName,
  46. siteConfig.title,
  47. siteConfig.touchIcon,
  48. ]);
  49. useEffect(() => {
  50. const handleRouteChange = (url: string) => {
  51. pageview(url);
  52. };
  53. router.events.on("routeChangeComplete", handleRouteChange);
  54. router.events.on("hashChangeComplete", handleRouteChange);
  55. return () => {
  56. router.events.off("routeChangeComplete", handleRouteChange);
  57. router.events.off("hashChangeComplete", handleRouteChange);
  58. };
  59. }, [router.events]);
  60. return (
  61. <>
  62. <SeoHead seoConfig={seoConfig}>
  63. <meta charSet="utf-8" />
  64. <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
  65. <meta
  66. name="viewport"
  67. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
  68. />
  69. <link rel="manifest" href="/manifest.json" />
  70. <link rel="icon" href="/logo.svg" type="image/svg+xml" />
  71. <link rel="icon" href="/favicon.ico" type="image/x-icon" />
  72. <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
  73. <link rel="mask-icon" href="/logo.svg" color="#000000" />
  74. <link
  75. href="/favicon-16x16.png"
  76. rel="icon"
  77. type="image/png"
  78. sizes="16x16"
  79. />
  80. <link
  81. href="/favicon-32x32.png"
  82. rel="icon"
  83. type="image/png"
  84. sizes="32x32"
  85. />
  86. <link rel="apple-touch-icon" href={siteConfig.touchIcon} />
  87. <meta name="apple-mobile-web-app-title" content={siteConfig.siteName} />
  88. <meta name="application-name" content={siteConfig.siteName} />
  89. <meta
  90. name="theme-color"
  91. content="#ffffff"
  92. media="(prefers-color-scheme: light)"
  93. data-react-helmet="true"
  94. />
  95. <meta
  96. name="theme-color"
  97. content="#111827"
  98. media="(prefers-color-scheme: dark)"
  99. data-react-helmet="true"
  100. />
  101. <meta data-rh="true" name="theme-color" content="#111827" />
  102. <meta name="msapplication-TileColor" content="#5b5b5b" />
  103. <meta name="msapplication-TileImage" content={siteConfig.touchIcon} />
  104. <meta name="msapplication-tooltip" content={siteConfig.title} />
  105. </SeoHead>
  106. <Script
  107. strategy="afterInteractive"
  108. src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
  109. />
  110. <Context.Provider value={{ genre, siteConfig }}>
  111. <SWRConfig value={{ fallback, revalidateIfStale: false }}>
  112. {Component.getLayout && !pageProps.statusCode ? (
  113. Component.getLayout(<Component {...otherProps} />)
  114. ) : (
  115. <Layout>
  116. <Component {...otherProps} />
  117. </Layout>
  118. )}
  119. </SWRConfig>
  120. </Context.Provider>
  121. </>
  122. );
  123. };
  124. MyApp.getInitialProps = async function (context: AppContext) {
  125. App.getInitialProps(context);
  126. const { data } = await get<GenreItem[]>("/api/genre/list");
  127. return {
  128. pageProps: {
  129. genre: data,
  130. fallback: { "/api/genre/list": data },
  131. siteConfig: getSiteConfig(context.ctx.req?.headers.host),
  132. },
  133. };
  134. };
  135. export default MyApp;