import 'reseter.css' import '../styles/globals.css' import type { AppProps } from 'next/app' import { default as Router } from 'next/router' import Script from 'next/script' import Head from 'next/head' import { RecoilRoot, useRecoilValue } from 'recoil' import { ThemeProvider } from 'styled-components' import { ReactElement, ReactNode, useEffect, useState } from 'react' import nProgress from 'nprogress' import { NextPage } from 'next' import { MotionConfig } from 'framer-motion' import isPropValid from '@emotion/is-prop-valid' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' import { SessionValue, SiteTheme } from '../globals/state' import { CustomGlobalStyle, GlobalStyle } from '../styles/global' import { HotEvent, HotEventSub, subscribeToHotEvent } from '../data/user/hotkeys' import { CommitHash, Environment, PaddleSandbox, PaddleVendorID } from '../globals/constants' import { getUserSetting, UserSettings } from '../data/user/settings' import { memoize } from '../util/util' type NextPageWithLayout = NextPage & { Layout?: (page: ReactElement) => ReactNode } type AppPropsWithErrAndLayout = AppProps & { err: any Component: NextPageWithLayout } Router.events.on('routeChangeStart', (route: string) => route.startsWith('/stories?id=') || nProgress.start()) Router.events.on( 'routeChangeComplete', (route: string) => route.startsWith('/stories?id=') || nProgress.done() ) Router.events.on('routeChangeError', () => nProgress.done()) if (Environment !== 'production') { // Silence duplicate recoil key warnings during hot reloads // see: https://github.com/facebookexperimental/Recoil/issues/733#issuecomment-925072943 const mutedConsole = memoize((console: Console) => ({ ...console, warn: (...args: any[]) => args[0]?.includes ? args[0]?.includes('Duplicate atom key') ? null : console.warn(...args) : console.warn(...args), })) global.console = mutedConsole(global.console) } function AppWithState({ Component, pageProps, err }: AppPropsWithErrAndLayout): JSX.Element { const siteTheme = useRecoilValue(SiteTheme) const settings = useRecoilValue(SessionValue('settings')) as UserSettings const [showBorder, setShowborder] = useState(false) const tabBorder = () => { setShowborder(true) return true } const checkClick = (e: MouseEvent) => { if (e.detail === 0) { // keyboard "click" event } else { // mouse "click" event setShowborder(false) } } useEffect(() => { subscribeToHotEvent( HotEvent.focusForward, new HotEventSub('indexTabF', tabBorder, false, false, false) ) subscribeToHotEvent(HotEvent.focusBack, new HotEventSub('indexTabB', tabBorder, false, false, false)) subscribeToHotEvent( HotEvent.preventEvent, new HotEventSub('indexPrevent', (e) => { e.preventDefault() return true }) ) }, []) useEffect(() => { document.addEventListener('mousedown', checkClick) return () => { document.removeEventListener('mousedown', checkClick) } }) useEffect(() => { if (typeof document === 'undefined') return const meta = document.createElement('meta') meta.name = 'darkreader' meta.content = 'disable' const observer = new MutationObserver(() => { const metaFake = document.querySelector('meta[content="' + meta.content + '"]') if (!metaFake) { document.head.append(meta) } const metaReal = document.querySelector('meta[name="' + meta.name + '"]') if (metaReal && (metaReal as HTMLMetaElement).content != meta.content) { metaReal.remove() } for (const style of document.head.querySelectorAll('.darkreader')) { style.remove() } }) observer.observe(document.head, { attributes: false, childList: true, subtree: false }) return () => { observer.disconnect() } }, []) const ComponentLayout = Component.Layout ?? ((page) => page) return ( <> {/* eslint-disable-next-line @typescript-eslint/ban-ts-comment */} {/* @ts-ignore */} {ComponentLayout()} ) } function App(props: AppPropsWithErrAndLayout): JSX.Element { return ( <> ) } export default App