_app.tsx 4.7 KB

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