_app.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import { SWRConfig } from "swr";
  2. import Head from "next/head";
  3. import Script from "next/script";
  4. import type { NextPage } from "next";
  5. import { useRouter } from "next/router";
  6. import type { AppProps } from "next/app";
  7. import App, { AppContext } from "next/app";
  8. import { ReactElement, ReactNode, useEffect } from "react";
  9. import { get } from "libs/http";
  10. import { pageview } from "libs/gtag";
  11. import { Context } from "libs/context";
  12. import { GA_TRACKING_ID } from "libs/config";
  13. import Layout from "components/common/Layout";
  14. import getSiteConfig, { SiteConfig } from "libs/getSiteConfig";
  15. import "styles/globals.scss";
  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. useEffect(() => {
  33. const handleRouteChange = (url: string) => {
  34. pageview(url);
  35. };
  36. router.events.on("routeChangeComplete", handleRouteChange);
  37. router.events.on("hashChangeComplete", handleRouteChange);
  38. return () => {
  39. router.events.off("routeChangeComplete", handleRouteChange);
  40. router.events.off("hashChangeComplete", handleRouteChange);
  41. };
  42. }, [router.events]);
  43. return (
  44. <>
  45. <Head>
  46. <meta charSet="utf-8" />
  47. <meta httpEquiv="X-UA-Compatible" content="IE=edge" />
  48. <meta
  49. name="viewport"
  50. content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no"
  51. />
  52. <title>{siteConfig.title}</title>
  53. <meta name="description" content={siteConfig.description} />
  54. <meta name="keywords" content={siteConfig.keywords} />
  55. <meta
  56. property="og:url"
  57. key="og:url"
  58. content={`https://${siteConfig.host}`}
  59. />
  60. <meta
  61. property="og:site_name"
  62. key="og:site_name"
  63. content={siteConfig.siteName}
  64. />
  65. <meta property="og:title" content={siteConfig.title} key="og:title" />
  66. <meta
  67. property="og:description"
  68. key="og:description"
  69. content={siteConfig.description}
  70. />
  71. <meta
  72. property="og:image"
  73. key="og:image"
  74. content={siteConfig.touchIcon}
  75. />
  76. <meta
  77. name="twitter:title"
  78. key="twitter:title"
  79. content={siteConfig.title}
  80. />
  81. <meta
  82. name="twitter:description"
  83. key="twitter:description"
  84. content={siteConfig.description}
  85. />
  86. <meta name="twitter:card" key="twitter:card" content="summary" />
  87. <meta
  88. name="twitter:image"
  89. key="twitter:image"
  90. content={siteConfig.touchIcon}
  91. />
  92. <link
  93. rel="canonical"
  94. key="canonical"
  95. href={`https://${siteConfig.host}`}
  96. />
  97. <link rel="manifest" href="/manifest.json" />
  98. <link rel="icon" href="/logo.svg" type="image/svg+xml" />
  99. <link rel="icon" href="/favicon.ico" type="image/x-icon" />
  100. <link rel="shortcut icon" href="/favicon.ico" type="image/x-icon" />
  101. <link rel="mask-icon" href="/logo.svg" color="#000000" />
  102. <link
  103. href="/favicon-16x16.png"
  104. rel="icon"
  105. type="image/png"
  106. sizes="16x16"
  107. />
  108. <link
  109. href="/favicon-32x32.png"
  110. rel="icon"
  111. type="image/png"
  112. sizes="32x32"
  113. />
  114. <link rel="apple-touch-icon" href={siteConfig.touchIcon} />
  115. <meta name="apple-mobile-web-app-title" content={siteConfig.siteName} />
  116. <meta name="application-name" content={siteConfig.siteName} />
  117. <meta
  118. name="theme-color"
  119. content="#ffffff"
  120. media="(prefers-color-scheme: light)"
  121. data-react-helmet="true"
  122. />
  123. <meta
  124. name="theme-color"
  125. content="#111827"
  126. media="(prefers-color-scheme: dark)"
  127. data-react-helmet="true"
  128. />
  129. <meta data-rh="true" name="theme-color" content="#111827" />
  130. <meta name="msapplication-TileColor" content="#5b5b5b" />
  131. <meta name="msapplication-TileImage" content={siteConfig.touchIcon} />
  132. <meta name="msapplication-tooltip" content={siteConfig.title} />
  133. <script
  134. type="application/ld+json"
  135. key="application/ld+json"
  136. dangerouslySetInnerHTML={{
  137. __html: JSON.stringify(siteConfig.jsonLd),
  138. }}
  139. />
  140. </Head>
  141. <Script
  142. strategy="afterInteractive"
  143. src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
  144. />
  145. <Context.Provider value={{ genre, siteConfig }}>
  146. <SWRConfig value={{ fallback, revalidateIfStale: false }}>
  147. {Component.getLayout && !pageProps.statusCode ? (
  148. Component.getLayout(<Component {...otherProps} />)
  149. ) : (
  150. <Layout>
  151. <Component {...otherProps} />
  152. </Layout>
  153. )}
  154. </SWRConfig>
  155. </Context.Provider>
  156. </>
  157. );
  158. };
  159. MyApp.getInitialProps = async function (context: AppContext) {
  160. App.getInitialProps(context);
  161. const { data } = await get<GenreItem[]>("/api/genre/list");
  162. return {
  163. pageProps: {
  164. genre: data,
  165. fallback: { "/api/genre/list": data },
  166. siteConfig: getSiteConfig(context.ctx.req?.headers.host),
  167. },
  168. };
  169. };
  170. export default MyApp;