From b195c9d6674ffbf075a713a74d19451232d20b71 Mon Sep 17 00:00:00 2001 From: Manuel S Date: Wed, 8 Jun 2022 00:20:46 +0200 Subject: [PATCH] feat: Added missing brackets (@theinternetftw) (#136) * Added bracket chart. * Added border radius to all sides of cards. * Added skeletons to home page. * Added support for system color theme. * Added code-splitting for GraphQL. * Added config constants. * Added warning banner on staging. * Added URL caching to API resolver (only for non-page request for now). * Made featured theme load by default. * Changed static path fetching to only fetch paths if not on staging (except for bracket pages). * Changed default color theme to system. * Moved color theme toggle from navigation to profile page. * Replaced sketchy local storage implementation with external package. * Fixed wrong fallback for featured theme on devices which don't support our codecs. * Fixed wrong links in footer. * Fixed code-splitting of Prism package. * Fixed "/" character not working in search input. * Fixed glitching multi cover image on playlist page. * Updated README.md. --- .env.development | 1 - .env.production | 1 - .eslintrc.json | 9 +- README.md | 53 +- next.config.js | 14 +- package-lock.json | 24 +- package.json | 3 +- src/components/bracket/BracketChart.js | 200 + .../bracket/BracketThemeSummaryCard.js | 82 + src/components/card/AnimeSummaryCard.js | 2 +- src/components/card/Card.js | 20 +- src/components/card/SummaryCard.js | 40 +- .../featured-theme/FeaturedTheme.js | 16 +- src/components/footer/Footer.js | 7 +- src/components/icon/CornerIcon.js | 13 + src/components/menu/ThemeMenu.js | 2 +- src/components/navigation/Navigation.js | 25 +- src/components/navigation/SearchNavigation.js | 15 +- src/components/skeleton/Skeleton.js | 32 + src/components/skeleton/index.js | 1 + src/components/table/Table.js | 2 +- src/components/utils/index.js | 1 - src/components/video-player/VideoPlayer.js | 4 +- src/context/colorThemeContext.js | 2 +- src/context/localPlaylistContext.js | 85 - src/context/watchHistoryContext.js | 77 - src/hooks/useColorTheme.js | 26 +- src/hooks/useLocalPlaylist.js | 37 + src/hooks/useSetting.js | 30 +- src/hooks/useWatchHistory.js | 37 + src/lib/client/announcement.js | 4 +- src/lib/client/api.js | 2 - src/lib/client/chunk.js | 14 + src/lib/client/index.js | 18 +- src/lib/client/randomGrill.js | 4 +- src/lib/client/randomTheme.js | 4 +- src/lib/client/search.js | 6 +- src/lib/common/animethemes/api.js | 24 +- src/lib/server/animebracket/brackets.json | 50 + src/lib/server/animebracket/mappings.json | 3365 +++++++++++++++-- src/lib/server/animebracket/resolvers.js | 26 +- src/lib/server/animebracket/type-defs.js | 1 + src/pages/_app.js | 22 +- src/pages/_document.js | 4 +- .../anime/[animeSlug]/[videoSlug]/index.js | 19 +- src/pages/anime/[animeSlug]/index.js | 11 +- src/pages/api/revalidate.js | 5 +- src/pages/design.js | 3 +- src/pages/event/[bracketSlug]/index.js | 123 +- src/pages/event/index.js | 60 +- src/pages/index.js | 12 +- src/pages/profile/gallery/index.js | 4 +- src/pages/profile/index.js | 24 +- src/pages/profile/playlist/index.js | 4 +- src/styles/global.js | 30 +- src/styles/mixins.js | 14 +- src/theme/index.js | 3 + src/utils/config.js | 51 + src/utils/fetchStaticPaths.js | 12 +- src/utils/settings.js | 10 +- src/utils/withBasePath.js | 4 +- 61 files changed, 4000 insertions(+), 794 deletions(-) delete mode 100644 .env.development delete mode 100644 .env.production create mode 100644 src/components/bracket/BracketChart.js create mode 100644 src/components/bracket/BracketThemeSummaryCard.js create mode 100644 src/components/icon/CornerIcon.js create mode 100644 src/components/skeleton/Skeleton.js create mode 100644 src/components/skeleton/index.js delete mode 100644 src/context/localPlaylistContext.js delete mode 100644 src/context/watchHistoryContext.js create mode 100644 src/hooks/useLocalPlaylist.js create mode 100644 src/hooks/useWatchHistory.js delete mode 100644 src/lib/client/api.js create mode 100644 src/lib/client/chunk.js create mode 100644 src/lib/server/animebracket/brackets.json create mode 100644 src/utils/config.js diff --git a/.env.development b/.env.development deleted file mode 100644 index 1affd3c..0000000 --- a/.env.development +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost diff --git a/.env.production b/.env.production deleted file mode 100644 index 5151fba..0000000 --- a/.env.production +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_API_URL=https://staging.animethemes.moe diff --git a/.eslintrc.json b/.eslintrc.json index 12eb4e0..581d7e5 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -31,14 +31,7 @@ } ], "no-duplicate-imports": "error", - "no-restricted-imports": [ - "error", - { - "patterns": [ - "components/*/*" - ] - } - ], + "no-unused-vars": "error", "react/react-in-jsx-scope": "off", "react/self-closing-comp": "error", // For now we don't habe prop-types validation diff --git a/README.md b/README.md index 8e2cdee..1fbbc9d 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,55 @@ ## Configuration To get started you need to define some environment variables. This can be done by creating a `.env.local` file in the -root directory. +root directory. A minimal setup only requires one variable to be set: + +```ini +; Set this to the URL on which your local API is served. +; +; (If you do not have a local instance of animethemes-server set up +; you can also use the production or staging URLs of the AnimeThemes API. +; Keep in mind though that this puts addtional load on our servers. +; That's why it's recommended to set up your own API instance locally.) +ANIMETHEMES_API_URL=http://localhost +``` + +To get to use all features of the client, you may configure some additional variables. This is a list of all available options: ```ini -; Back-end API configuration -; These values are required. +; ===== Server-side ===== + +; The URL to the AnimeThemes API which will be used on the server. ANIMETHEMES_API_URL=http://localhost + +; If specified, this API key will be used to make requests to the AnimeThemes API. +; This is used to by-pass rate limiting. ANIMETHEMES_API_KEY=... -; (Optional) The URL to the AnimeThemes API to use on the front-end. -; For development this is "http://localhost" by default. -; For production this is "https://staging.animethemes.moe" by default. -NEXT_PUBLIC_API_URL=http://localhost +; The token to use for manual revalidation. +REVALIDATE_TOKEN=secret -; (Optional) The URL from which video files should be served. -; By default this is "https://animethemes.moe". -NEXT_PUBLIC_VIDEO_URL=https://animethemes.moe +; Set to any truthy value to activate the bundle analyzer. +ANALYZE=true -; (Optional) The base path the app should be hosted on. -; By default the app is hosted on the root path (/). +; ===== Server-side + Client-side ===== + +; The base path the app should be hosted on. NEXT_PUBLIC_BASE_PATH=/wiki + +; The URL to the AnimeThemes API which will be used on the browser. +NEXT_PUBLIC_API_URL=https://api.animethemes.moe + +; The URL from which video files should be served. +NEXT_PUBLIC_VIDEO_URL=https://v.animethemes.moe + +; The URL to use for app links. +NEXT_PUBLIC_APP_URL=https://app.animethemes.moe + +; Set to any truthy value to activate staging mode. +; Staging mode only pre-renders a small subset of pages to reduce build time. +STAGING=true ``` For more information on environment variables see the [Next.js documentation](https://nextjs.org/docs/basic-features/environment-variables). @@ -56,4 +83,4 @@ For more information on environment variables see the [Next.js documentation](ht ### APIs -- [AnimeThemes API](https://staging.animethemes.moe/api/docs/) +- [AnimeThemes API](https://api-docs.animethemes.moe/) diff --git a/next.config.js b/next.config.js index 1d2fccb..f9bec92 100644 --- a/next.config.js +++ b/next.config.js @@ -1,15 +1,21 @@ -const { info } = require("next/dist/build/output/log"); +const { info, error } = require("next/dist/build/output/log"); +const { ANALYZE, STAGING, BASE_PATH, validateConfig } = require("./src/utils/config"); const withBundleAnalyzer = require("@next/bundle-analyzer")({ - enabled: process.env.ANALYZE === "true", + enabled: ANALYZE, }); -if (process.env.NEXT_PUBLIC_STAGING) { +if (!validateConfig()) { + error("Shutting down because of invalid configuration..."); + return process.exit(1); +} + +if (STAGING) { info("Running animethemes-web in staging mode!"); } module.exports = withBundleAnalyzer({ - basePath: process.env.NEXT_PUBLIC_BASE_PATH || "", + basePath: BASE_PATH, reactStrictMode: true, compiler: { styledComponents: true diff --git a/package-lock.json b/package-lock.json index 541c428..ec36e60 100644 --- a/package-lock.json +++ b/package-lock.json @@ -35,7 +35,8 @@ "rehype-react": "^7.0.4", "sass": "^1.43.4", "styled-components": "^5.3.3", - "unified": "^10.1.2" + "unified": "^10.1.2", + "use-local-storage-state": "^17.2.0" }, "devDependencies": { "eslint": "7.32.0", @@ -4400,6 +4401,21 @@ "punycode": "^2.1.0" } }, + "node_modules/use-local-storage-state": { + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/use-local-storage-state/-/use-local-storage-state-17.2.0.tgz", + "integrity": "sha512-CcnSJ3oaCbb4aXPRpctXNoAOfJ1V4+OWLHrvA2s2hOYCE7HG0QEyKd324XAs7FsyXv/uzYcu7sPxn+KwvJIwlw==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/astoilkov" + }, + "peerDependencies": { + "react": ">=16.8.0 < 18", + "react-dom": ">=16.8.0 < 18" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -7733,6 +7749,12 @@ "punycode": "^2.1.0" } }, + "use-local-storage-state": { + "version": "17.2.0", + "resolved": "https://registry.npmjs.org/use-local-storage-state/-/use-local-storage-state-17.2.0.tgz", + "integrity": "sha512-CcnSJ3oaCbb4aXPRpctXNoAOfJ1V4+OWLHrvA2s2hOYCE7HG0QEyKd324XAs7FsyXv/uzYcu7sPxn+KwvJIwlw==", + "requires": {} + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index 313691b..6231e4d 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,8 @@ "rehype-react": "^7.0.4", "sass": "^1.43.4", "styled-components": "^5.3.3", - "unified": "^10.1.2" + "unified": "^10.1.2", + "use-local-storage-state": "^17.2.0" }, "devDependencies": { "eslint": "7.32.0", diff --git a/src/components/bracket/BracketChart.js b/src/components/bracket/BracketChart.js new file mode 100644 index 0000000..20c70a0 --- /dev/null +++ b/src/components/bracket/BracketChart.js @@ -0,0 +1,200 @@ +import styled from "styled-components"; +import { Text } from "components/text"; +import { faDiagramProject } from "@fortawesome/pro-solid-svg-icons"; +import { Icon } from "components/icon"; +import { memo, useCallback, useRef, useState } from "react"; +import theme from "theme"; +import { motion } from "framer-motion"; +import { Button } from "components/button"; +import { BracketThemeSummaryCard } from "components/bracket/BracketThemeSummaryCard"; + +const StyledBracketContainer = styled.div` + flex: 1 1 0; + + overflow: hidden; + user-select: none; + + cursor: grab; + + &:active { + cursor: grabbing; + } + + &:fullscreen { + background-color: ${theme.colors["background"]}; + } +`; + +const StyledBracket = styled(motion.div)` + display: flex; + position: relative; + + width: min-content; + gap: 128px; + padding: 16px; +`; + +const StyledCanvas = styled.canvas` + position: absolute; + inset: 0; + width: 100%; + height: 100%; +`; + +const StyledRound = styled.div` + display: flex; + flex-direction: column; + gap: 64px; +`; + +const StyledPairing = styled.div` + flex: 1; + + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 8px; +`; + +const StyledBracketThemeSummaryCard = styled(BracketThemeSummaryCard)` + width: 384px; +`; + +export function BracketChart({ bracket }) { + const [showBracketChart, setShowBracketChart] = useState(false); + const bracketRef = useRef(); + + const onBracketInit = useCallback((bracket) => { + bracketRef.current = bracket; + + if (bracket) { + bracket.requestFullscreen(); + + bracket.addEventListener("fullscreenchange", onFullscreenChange); + + function onFullscreenChange() { + if (!document.fullscreenElement) { + setShowBracketChart(false); + bracket.removeEventListener("fullscreenchange", onFullscreenChange); + } + } + } + }, []); + + const onCanvasInit = useCallback((canvas) => { + if (!canvas) { + return; + } + + const parentRect = canvas.parentNode.getBoundingClientRect(); + + canvas.width = parentRect.width; + canvas.height = parentRect.height; + + /** @type CanvasRenderingContext2D */ + const ctx = canvas.getContext("2d"); + + ctx.strokeStyle = "#75ead4"; + ctx.lineWidth = 2; + ctx.translate(-parentRect.left, -parentRect.top); + + const cards = document.querySelectorAll("[data-theme]"); + const cardsById = [...cards].reduce((map, card) => { + const id = card.dataset.theme; + if (!map[id]) { + map[id] = []; + } + map[id].push(card); + return map; + }, {}); + + for (const cards of Object.values(cardsById)) { + for (let i = 0; i < cards.length - 1; i++) { + const current = cards[i]; + const currentRect = current.getBoundingClientRect(); + const next = cards[i + 1]; + const nextRect = next.getBoundingClientRect(); + ctx.beginPath(); + ctx.moveTo(currentRect.left + currentRect.width, currentRect.top + currentRect.height / 2); + ctx.bezierCurveTo( + nextRect.left - (nextRect.left - (currentRect.left + currentRect.width)) / 2, + currentRect.top + currentRect.height / 2, + nextRect.left - (nextRect.left - (currentRect.left + currentRect.width)) / 2, + nextRect.top + nextRect.height / 2, + nextRect.left, + nextRect.top + nextRect.height / 2 + ); + ctx.stroke(); + } + } + }, []); + + return ( + <> + + {showBracketChart ? ( + + + + {bracket.rounds.sort((a, b) => a.tier - b.tier).map((round) => )} + + + ) : null} + + ); +} + +const BracketRound = memo(function BracketRound({ round }) { + if (!round.pairings || !round.pairings.length) { + return null; + } + + return ( + + {round.name} + + + ); +}); + +function BracketPairings({ pairings }) { + return pairings.sort((a, b) => (a.group - b.group) || (a.order - b.order)).map((pairing, index) => ( + + + VS + + + )); +} + +function ContestantCard({ contestant, opponent, contestantVotes, opponentVotes }) { + const isVoted = !!contestantVotes; + const isWinner = isVoted && (contestantVotes !== opponentVotes ? contestantVotes > opponentVotes : contestant.seed < opponent.seed); + + return ( + + ); +} diff --git a/src/components/bracket/BracketThemeSummaryCard.js b/src/components/bracket/BracketThemeSummaryCard.js new file mode 100644 index 0000000..ce884ce --- /dev/null +++ b/src/components/bracket/BracketThemeSummaryCard.js @@ -0,0 +1,82 @@ +import styled from "styled-components"; +import { SummaryCard } from "components/card"; +import { Text } from "components/text"; +import useImage from "hooks/useImage"; +import createVideoSlug from "utils/createVideoSlug"; +import Link from "next/link"; +import { SongTitleWithArtists } from "components/utils"; +import { Column } from "components/box"; +import { Icon } from "components/icon"; +import { faAward, faSeedling, faUsers } from "@fortawesome/pro-solid-svg-icons"; +import { CornerIcon } from "components/icon/CornerIcon"; + +const StyledSummaryCardWrapper = styled.div` + position: relative; + + justify-self: stretch; +`; + +const StyledSummaryCard = styled(SummaryCard)` + padding-inline-end: 24px; + + opacity: var(--opacity); +`; + +const StyledRank = styled(Text)` + font-variant-numeric: tabular-nums; + letter-spacing: 1px; +`; + +export function BracketThemeSummaryCard({ contestant, isVoted, isWinner, seed, votes, ...props }) { + const theme = contestant.theme; + const { smallCover } = useImage(theme?.anime); + + let to = null; + let description = contestant.source; + + if (theme) { + const entry = theme.entries?.[0]; + const video = entry?.videos?.[0]; + const videoSlug = createVideoSlug(theme, entry, video); + + to = `/anime/${theme.anime.slug}/${videoSlug}`; + + description = ( + + {theme.slug} + + {theme.anime.name} + + + ); + } + + return ( + + : contestant.name} + description={description} + image={smallCover} + to={to} + style={{ "--opacity": !isVoted || isWinner ? 1 : 0.5 }} + > + + + + {seed} + + {isVoted && ( + + + {votes} + + )} + + + + {isWinner && ( + + )} + + ); +} diff --git a/src/components/card/AnimeSummaryCard.js b/src/components/card/AnimeSummaryCard.js index 4fde5b2..a2e437d 100644 --- a/src/components/card/AnimeSummaryCard.js +++ b/src/components/card/AnimeSummaryCard.js @@ -57,7 +57,7 @@ const StyledThemeGroupContainer = styled.div` margin-top: 8px; `; -export function AnimeSummaryCard({ anime, previewThemes = false, expandable = false, ...props }) { +export function AnimeSummaryCard({ anime, expandable = false, ...props }) { const [isExpanded, toggleExpanded] = useToggle(); const { smallCover } = useImage(anime); const isMobile = useMediaQuery(`(max-width: ${theme.breakpoints.mobileMax})`); diff --git a/src/components/card/Card.js b/src/components/card/Card.js index d1cb750..fcef080 100644 --- a/src/components/card/Card.js +++ b/src/components/card/Card.js @@ -5,12 +5,11 @@ import { withHover } from "styles/mixins"; export const Card = styled(Solid)` display: block; + position: relative; - padding: 16px 24px; - border-color: ${(props) => theme.colors[props.color] || props.color || theme.colors["text-primary"]}; - border-left-width: 4px; - border-left-style: solid; - border-radius: 0 8px 8px 0; + padding: 16px 24px 16px 28px; + border-radius: ${theme.scalars.borderRadiusCard}; + overflow: hidden; box-shadow: ${theme.shadows.medium}; @@ -21,4 +20,15 @@ export const Card = styled(Solid)` background-color: ${theme.colors["solid-on-card"]}; `)} `} + + &:before { + content: " "; + display: block; + position: absolute; + top: 0; + left: 0; + width: 4px; + height: 100%; + background-color: ${(props) => theme.colors[props.color] || props.color || theme.colors["text-primary"]}; + } `; diff --git a/src/components/card/SummaryCard.js b/src/components/card/SummaryCard.js index 20eabc3..c0c6ff7 100644 --- a/src/components/card/SummaryCard.js +++ b/src/components/card/SummaryCard.js @@ -14,23 +14,21 @@ const StyledSummaryCard = styled(Card)` gap: 16px; height: 64px; - padding: 0 1rem 0 0; + padding: 0 1rem 0 4px; `; const StyledCover = styled.img.attrs({ loading: "lazy" })` - width: 48px; - height: 64px; - object-fit: cover; - - ${loadingAnimation} - - ${(props) => props.isPlaceholder && css` - padding: 0.5rem; - object-fit: contain; - background: white; - `} + width: 48px; + height: 64px; + object-fit: cover; + + ${(props) => props.isPlaceholder ? css` + padding: 0.5rem; + object-fit: contain; + background: white; + ` : loadingAnimation} `; const StyledBody = styled(Column)` @@ -46,7 +44,7 @@ export function SummaryCard({ title, description, image, to, children, ...props return ( - + setImageNotFound(true)} /> - + {typeof title === "string" ? ( - + {title} - + ) : title} {!!description && ( @@ -80,6 +78,16 @@ export function SummaryCard({ title, description, image, to, children, ...props ); } +function ConditionalWrapper({ as: Wrapper, condition, ...props }) { + if (condition) { + return ( + + ); + } + + return props.children; +} + SummaryCard.Description = function SummaryCardDescription({ children }) { return ( <> diff --git a/src/components/featured-theme/FeaturedTheme.js b/src/components/featured-theme/FeaturedTheme.js index 27f4088..68ad6ad 100644 --- a/src/components/featured-theme/FeaturedTheme.js +++ b/src/components/featured-theme/FeaturedTheme.js @@ -10,8 +10,7 @@ import gql from "graphql-tag"; import { fetchRandomGrill } from "lib/client/randomGrill"; import createVideoSlug from "utils/createVideoSlug"; import Link from "next/link"; - -const videoBaseUrl = process.env.NEXT_PUBLIC_VIDEO_URL || "https://animethemes.moe"; +import { VIDEO_URL } from "utils/config"; const slowPan = keyframes` from { @@ -75,6 +74,7 @@ const StyledCenter = styled.div` const StyledVideo = styled.video` width: 100%; filter: blur(5px); + background-color: ${theme.colors["solid-on-card"]}; `; const StyledCover = styled.img` @@ -152,7 +152,7 @@ export function FeaturedTheme({ theme }) { function FeaturedThemeBackground({ theme }) { const [ featuredThemePreview ] = useSetting(FeaturedThemePreview); - const { canPlayVideo } = useCompatability({ canPlayVideo: false }); + const { canPlayVideo } = useCompatability(); const [ fallbackToCover, setFallbackToCover ] = useState(false); const { smallCover: featuredCover } = useImage(theme.anime); @@ -180,16 +180,20 @@ function FeaturedThemeBackground({ theme }) { setFallbackToCover(true)} - /> + > + - + Transparency @@ -60,10 +61,10 @@ export function Footer() { - + Terms of Service - + Privacy Policy diff --git a/src/components/icon/CornerIcon.js b/src/components/icon/CornerIcon.js new file mode 100644 index 0000000..9ca856d --- /dev/null +++ b/src/components/icon/CornerIcon.js @@ -0,0 +1,13 @@ +import styled from "styled-components"; +import { Icon } from "components/icon/Icon"; +import theme from "theme"; + +export const CornerIcon = styled(Icon).attrs({ + size: "2x" +})` + position: absolute; + right: 0; + top: 0; + color: ${theme.colors["text-primary"]}; + transform: translate(50%, -33%) rotate(10deg); +`; diff --git a/src/components/menu/ThemeMenu.js b/src/components/menu/ThemeMenu.js index 6fa7e72..4d1d4f1 100644 --- a/src/components/menu/ThemeMenu.js +++ b/src/components/menu/ThemeMenu.js @@ -2,7 +2,7 @@ import { Icon } from "components/icon"; import { faMinus, faPlus } from "@fortawesome/pro-solid-svg-icons"; import { Text } from "components/text"; import { Menu } from "components/menu"; -import { useLocalPlaylist } from "context/localPlaylistContext"; +import useLocalPlaylist from "hooks/useLocalPlaylist"; import gql from "graphql-tag"; export function ThemeMenu({ theme }) { diff --git a/src/components/navigation/Navigation.js b/src/components/navigation/Navigation.js index 1e540ba..2c0fb8f 100644 --- a/src/components/navigation/Navigation.js +++ b/src/components/navigation/Navigation.js @@ -1,16 +1,6 @@ -import { useContext, useState } from "react"; +import { useState } from "react"; import Link from "next/link"; -import { - faBars, - faLightbulb, - faMoon, - faRandom, - faSearch, - faSpinner, - faTimes, - faTv, - faUser -} from "@fortawesome/pro-solid-svg-icons"; +import { faBars, faRandom, faSearch, faTimes, faTv, faUser } from "@fortawesome/pro-solid-svg-icons"; import { StyledCollapsibleLink, StyledLogo, @@ -21,7 +11,6 @@ import { StyledNavigationLinks } from "./Navigation.style"; import { Button } from "components/button"; -import ColorThemeContext from "context/colorThemeContext"; import { Icon } from "components/icon"; import { Text } from "components/text"; import useCurrentSeason from "hooks/useCurrentSeason"; @@ -30,7 +19,6 @@ import { useRouter } from "next/router"; export function Navigation({ offsetToggleButton = false }) { const [ show, setShow ] = useState(false); - const { colorTheme, toggleColorTheme } = useContext(ColorThemeContext); const { currentYear, currentSeason } = useCurrentSeason(); const router = useRouter(); @@ -76,15 +64,6 @@ export function Navigation({ offsetToggleButton = false }) { My Profile - diff --git a/src/components/navigation/SearchNavigation.js b/src/components/navigation/SearchNavigation.js index 7184d84..fd8ec04 100644 --- a/src/components/navigation/SearchNavigation.js +++ b/src/components/navigation/SearchNavigation.js @@ -43,6 +43,7 @@ const updateSearchQuery = debounce((router, newSearchQuery) => { export function SearchNavigation() { const router = useRouter(); + const [routerWasReady, setRouterWasReady] = useState(router.isReady); const { entity, ...query } = router.query; const { q: initialSearchQuery = "" } = query; @@ -53,12 +54,6 @@ export function SearchNavigation() { updateSearchQuery(router, newInputSearchQuery); }; - useEffect(() => { - if (router.isReady) { - setInputSearchQuery(initialSearchQuery); - } - }, [router.isReady]); - const inputRef = useRef(); const onMountInput = useCallback((input) => { @@ -74,7 +69,7 @@ export function SearchNavigation() { useEffect(() => { const hotkeyListener = (event) => { - if ((event.key === "s" && event.ctrlKey) || (event.key === "/")) { + if (inputRef.current !== document.activeElement && ((event.key === "s" && event.ctrlKey) || (event.key === "/"))) { event.preventDefault(); inputRef.current?.focus({ preventScroll: true @@ -88,6 +83,12 @@ export function SearchNavigation() { return () => window.removeEventListener("keydown", hotkeyListener); }, []); + if (router.isReady !== routerWasReady) { + setRouterWasReady(router.isReady); + setInputSearchQuery(initialSearchQuery); + return null; + } + return ( <> Search diff --git a/src/components/skeleton/Skeleton.js b/src/components/skeleton/Skeleton.js new file mode 100644 index 0000000..0ceb384 --- /dev/null +++ b/src/components/skeleton/Skeleton.js @@ -0,0 +1,32 @@ +import styled from "styled-components"; +import { loadingAnimation } from "styles/mixins"; +import { fadeIn } from "styles/animations"; +import theme from "theme"; + +const StyledSkeleton = styled.div` + ${loadingAnimation} +`; + +const StyledSkeletonSummaryCard = styled(StyledSkeleton)` + height: 64px; + border-radius: ${theme.scalars.borderRadiusCard}; +`; + +const StyledContent = styled.div` + animation: ${fadeIn} 500ms var(--delay) both; +`; + +export function Skeleton({ children, variant, delay = 0 }) { + if (!children) { + switch (variant) { + case "summary-card": + return ; + } + } + + return ( + + {children} + + ); +} diff --git a/src/components/skeleton/index.js b/src/components/skeleton/index.js new file mode 100644 index 0000000..0ea7701 --- /dev/null +++ b/src/components/skeleton/index.js @@ -0,0 +1 @@ +export { Skeleton } from "./Skeleton"; diff --git a/src/components/table/Table.js b/src/components/table/Table.js index 36149ee..5a475e2 100644 --- a/src/components/table/Table.js +++ b/src/components/table/Table.js @@ -6,7 +6,7 @@ export const Table = styled.div``; Table.Body = styled.div` border: 2px solid ${theme.colors["solid-on-card"]}; - border-radius: 8px; + border-radius: ${theme.scalars.borderRadiusCard}; overflow: hidden; `; diff --git a/src/components/utils/index.js b/src/components/utils/index.js index acd2843..92cff4b 100644 --- a/src/components/utils/index.js +++ b/src/components/utils/index.js @@ -5,5 +5,4 @@ export { SongTitle } from "./SongTitle"; export { Performances } from "./Performances"; export { SongTitleWithArtists } from "./SongTitleWithArtists"; export { HorizontalScroll } from "./HorizontalScroll"; -export { Highlight } from "./Highlight"; export { ErrorBoundary } from "./ErrorBoundary"; diff --git a/src/components/video-player/VideoPlayer.js b/src/components/video-player/VideoPlayer.js index d04d5bd..9354d9d 100644 --- a/src/components/video-player/VideoPlayer.js +++ b/src/components/video-player/VideoPlayer.js @@ -19,12 +19,12 @@ import { Column, Row } from "components/box"; import { Container } from "components/container"; import useCompatability from "hooks/useCompatability"; import { useRouter } from "next/router"; -import { videoBaseUrl } from "lib/client/api"; import useMediaQuery from "hooks/useMediaQuery"; import styledTheme from "theme"; import { SongTitle } from "components/utils"; import useSetting from "hooks/useSetting"; import { GlobalVolume } from "utils/settings"; +import { VIDEO_URL } from "utils/config"; export function VideoPlayer({ anime, theme, entry, video, background, ...props }) { const [isPlaying, setPlaying] = useState(false); @@ -33,7 +33,7 @@ export function VideoPlayer({ anime, theme, entry, video, background, ...props } const progressRef = useRef(); const { clearCurrentVideo } = useContext(PlayerContext); const isMobile = useMediaQuery(`(max-width: ${styledTheme.breakpoints.mobileMax})`); - const videoUrl = `${videoBaseUrl}/${video.basename}`; + const videoUrl = `${VIDEO_URL}/${video.basename}`; const router = useRouter(); const [globalVolume, setGlobalVolume] = useSetting(GlobalVolume); diff --git a/src/context/colorThemeContext.js b/src/context/colorThemeContext.js index 00ae954..e283b44 100644 --- a/src/context/colorThemeContext.js +++ b/src/context/colorThemeContext.js @@ -2,7 +2,7 @@ import { createContext } from "react"; const ColorThemeContext = createContext({ colorTheme: null, - toggleColorTheme: () => {} + setColorTheme: () => {} }); export default ColorThemeContext; diff --git a/src/context/localPlaylistContext.js b/src/context/localPlaylistContext.js deleted file mode 100644 index 7342d85..0000000 --- a/src/context/localPlaylistContext.js +++ /dev/null @@ -1,85 +0,0 @@ -import { createContext, useCallback, useContext, useEffect, useState } from "react"; -import { useToasts } from "context/toastContext"; -import { PlaylistAddToast } from "components/toast"; -import { ThemeSummaryCard } from "components/card"; - -const LocalPlaylistContext = createContext(); - -export function LocalPlaylistProvider({ children }) { - const { dispatchToast } = useToasts(); - const [ localPlaylist, setLocalPlaylist ] = useState([]); - - const reload = useCallback(() => setLocalPlaylist(load()), [ setLocalPlaylist ]); - - const addToPlaylist = useCallback((theme) => { - ThemeSummaryCard.fetchData(theme.id).then((themeFiltered) => { - const localPlaylist = load(); - - localPlaylist.push(themeFiltered); - - save(localPlaylist); - reload(); - - dispatchToast(theme.id, ); - }); - }, [ dispatchToast, reload ]); - - const removeFromPlaylist = useCallback((theme) => { - const localPlaylist = load(); - - const index = localPlaylist.findIndex((t) => t.id === theme.id); - if (index !== -1) { - localPlaylist.splice(index, 1); - } - - save(localPlaylist); - reload(); - }, [ reload ]); - - const isInPlaylist = useCallback((theme) => !!localPlaylist.find((t) => t.id === theme.id), [ localPlaylist ]); - - const setPlaylist = useCallback((playlist) => { - save(playlist); - reload(); - }, [ reload ]); - - useEffect(() => { - reload(); - - window.addEventListener("storage", reload); - - return () => window.removeEventListener("storage", reload); - }, [ reload ]); - - const value = { - localPlaylist, - addToPlaylist, - removeFromPlaylist, - isInPlaylist, - setPlaylist - }; - - return ( - - {children} - - ); -} - -export function useLocalPlaylist() { - return useContext(LocalPlaylistContext); -} - -function load() { - const raw = window.localStorage.getItem("local-playlist"); - - if (raw) { - return JSON.parse(raw); - } - - return []; -} - -function save(localPlaylist) { - window.localStorage.setItem("local-playlist", JSON.stringify(localPlaylist)); -} diff --git a/src/context/watchHistoryContext.js b/src/context/watchHistoryContext.js deleted file mode 100644 index 0327452..0000000 --- a/src/context/watchHistoryContext.js +++ /dev/null @@ -1,77 +0,0 @@ -import { createContext, useCallback, useContext, useEffect, useState } from "react"; -import { ThemeSummaryCard } from "components/card"; - -const WatchHistoryContext = createContext(); - -export function WatchHistoryProvider({ children }) { - const [ history, setHistory ] = useState([]); - - const reload = useCallback(() => setHistory(load()), [ setHistory ]); - - const addToHistory = useCallback((theme) => { - ThemeSummaryCard.fetchData(theme.id).then((themeFiltered) => { - let history = load(); - - // Don't add if the most recent entry is the same as the new one - if (history[history.length - 1]?.id === theme.id) { - return; - } - - // Remove all previous occurences of the theme to avoid duplicates - history = history.filter((t) => t.id !== theme.id); - - history.push(themeFiltered); - - // Keep history below 100 entries - if (history.length > 100) { - history.shift(); - } - - save(history); - reload(); - }); - }, [ reload ]); - - const clearHistory = useCallback(() => { - save([]); - reload(); - }, [ reload ]); - - useEffect(() => { - reload(); - - window.addEventListener("storage", reload); - - return () => window.removeEventListener("storage", reload); - }, [ reload ]); - - const value = { - history, - addToHistory, - clearHistory - }; - - return ( - - {children} - - ); -} - -export function useWatchHistory() { - return useContext(WatchHistoryContext); -} - -function load() { - const raw = window.localStorage.getItem("history"); - - if (raw) { - return JSON.parse(raw); - } - - return []; -} - -function save(history) { - window.localStorage.setItem("history", JSON.stringify(history)); -} diff --git a/src/hooks/useColorTheme.js b/src/hooks/useColorTheme.js index b4195fd..eda3850 100644 --- a/src/hooks/useColorTheme.js +++ b/src/hooks/useColorTheme.js @@ -1,24 +1,16 @@ -import { useEffect, useState } from "react"; +import { useEffect } from "react"; +import useLocalStorageState from "use-local-storage-state"; export default function useColorTheme() { - const [ theme, setTheme ] = useState(null); + const [ theme, setTheme ] = useLocalStorageState("theme", { ssr: true }, "system"); useEffect(() => { - const body = document.body; + setTheme(document.body.getAttribute("theme")); + }, [setTheme]); - setTheme(body.getAttribute("theme")); - }, []); + useEffect(() => { + document.body.setAttribute("theme", theme); + }, [theme]); - function toggleTheme() { - const newTheme = theme === "dark" ? "light" : "dark"; - - setTheme(newTheme); - - const body = document.body; - body.setAttribute("theme", newTheme); - - window.localStorage.setItem("theme", newTheme); - } - - return [theme, toggleTheme]; + return [theme, setTheme]; } diff --git a/src/hooks/useLocalPlaylist.js b/src/hooks/useLocalPlaylist.js new file mode 100644 index 0000000..17b2e1e --- /dev/null +++ b/src/hooks/useLocalPlaylist.js @@ -0,0 +1,37 @@ +import useLocalStorageState from "use-local-storage-state"; +import { ThemeSummaryCard } from "components/card"; +import { PlaylistAddToast } from "components/toast"; +import { useToasts } from "context/toastContext"; + +export default function useLocalPlaylist() { + const [ localPlaylist, setLocalPlaylist ] = useLocalStorageState("local-playlist", { ssr: true, defaultValue: [] }); + const { dispatchToast } = useToasts(); + + function addToPlaylist(theme) { + ThemeSummaryCard.fetchData(theme.id).then((themeFiltered) => { + setLocalPlaylist([ ...localPlaylist, themeFiltered ]); + + dispatchToast(theme.id, ); + }); + } + + function removeFromPlaylist(theme) { + setLocalPlaylist(localPlaylist.filter((t) => t.id !== theme.id)); + } + + function isInPlaylist(theme) { + return !!localPlaylist.find((t) => t.id === theme.id); + } + + function setPlaylist(playlist) { + setLocalPlaylist(playlist); + } + + return { + localPlaylist, + addToPlaylist, + removeFromPlaylist, + isInPlaylist, + setPlaylist, + }; +} diff --git a/src/hooks/useSetting.js b/src/hooks/useSetting.js index d208c14..4743b70 100644 --- a/src/hooks/useSetting.js +++ b/src/hooks/useSetting.js @@ -1,31 +1,7 @@ -import { useEffect, useState } from "react"; +import useLocalStorageState from "use-local-storage-state"; export default function useSetting({ __KEY__: key, __INITIAL_VALUE__: initialValue }) { - const [setting, setSetting] = useState(initialValue); + const [setting, setSetting] = useLocalStorageState(key, { ssr: true, defaultValue: initialValue }); - useEffect(() => { - function reload() { - const localSetting = window.localStorage.getItem(key); - if (localSetting !== null) { - setSetting(localSetting); - } - } - - reload(); - - window.addEventListener("storage", reload); - - return () => window.removeEventListener("storage", reload); - }, [ key ]); - - function updateSetting(value) { - setSetting(value); - - window.localStorage.setItem(key, value); - } - - return [ - setting, - updateSetting - ]; + return [setting, setSetting]; } diff --git a/src/hooks/useWatchHistory.js b/src/hooks/useWatchHistory.js new file mode 100644 index 0000000..6edcb67 --- /dev/null +++ b/src/hooks/useWatchHistory.js @@ -0,0 +1,37 @@ +import useLocalStorageState from "use-local-storage-state"; +import { ThemeSummaryCard } from "components/card"; + +export default function useLocalPlaylist() { + const [ history, setHistory ] = useLocalStorageState("history", { ssr: true, defaultValue: [] }); + + function addToHistory(theme) { + ThemeSummaryCard.fetchData(theme.id).then((themeFiltered) => { + // Don't add if the most recent entry is the same as the new one + if (history[history.length - 1]?.id === theme.id) { + return; + } + + // Remove all previous occurences of the theme to avoid duplicates + const newHistory = history.filter((t) => t.id !== theme.id); + + newHistory.push(themeFiltered); + + // Keep history below 100 entries + if (newHistory.length > 100) { + newHistory.shift(); + } + + setHistory(newHistory); + }); + } + + function clearHistory() { + setHistory([]); + } + + return { + history, + addToHistory, + clearHistory, + }; +} diff --git a/src/lib/client/announcement.js b/src/lib/client/announcement.js index c3e2f2d..5621fc6 100644 --- a/src/lib/client/announcement.js +++ b/src/lib/client/announcement.js @@ -1,7 +1,7 @@ -import { baseUrl } from "lib/client/api"; +import { CLIENT_API_URL } from "utils/config"; export async function fetchAnnouncements() { - const res = await fetch(`${baseUrl}/announcement`); + const res = await fetch(`${CLIENT_API_URL}/announcement`); const json = await res.json(); return json.announcements; diff --git a/src/lib/client/api.js b/src/lib/client/api.js deleted file mode 100644 index c894499..0000000 --- a/src/lib/client/api.js +++ /dev/null @@ -1,2 +0,0 @@ -export const baseUrl = process.env.NEXT_PUBLIC_API_URL; -export const videoBaseUrl = process.env.NEXT_PUBLIC_VIDEO_URL || "https://animethemes.moe"; diff --git a/src/lib/client/chunk.js b/src/lib/client/chunk.js new file mode 100644 index 0000000..e971d0c --- /dev/null +++ b/src/lib/client/chunk.js @@ -0,0 +1,14 @@ +import { makeExecutableSchema } from "@graphql-tools/schema"; + +import { buildFetchData } from "lib/common"; + +import typeDefsAnimeThemes from "lib/common/animethemes/type-defs"; + +import resolversAnimeThemes from "lib/common/animethemes/resolvers"; + +const schema = makeExecutableSchema({ + typeDefs: typeDefsAnimeThemes, + resolvers: resolversAnimeThemes +}); + +export const fetchDataClient = buildFetchData(schema); diff --git a/src/lib/client/index.js b/src/lib/client/index.js index e971d0c..a4c3429 100644 --- a/src/lib/client/index.js +++ b/src/lib/client/index.js @@ -1,14 +1,6 @@ -import { makeExecutableSchema } from "@graphql-tools/schema"; +export async function fetchDataClient(...args) { + // Code-split anything GraphQL related to reduce initial bundle size. + const { fetchDataClient } = await import("./chunk"); -import { buildFetchData } from "lib/common"; - -import typeDefsAnimeThemes from "lib/common/animethemes/type-defs"; - -import resolversAnimeThemes from "lib/common/animethemes/resolvers"; - -const schema = makeExecutableSchema({ - typeDefs: typeDefsAnimeThemes, - resolvers: resolversAnimeThemes -}); - -export const fetchDataClient = buildFetchData(schema); + return fetchDataClient(...args); +} diff --git a/src/lib/client/randomGrill.js b/src/lib/client/randomGrill.js index 65eb7de..67a9a06 100644 --- a/src/lib/client/randomGrill.js +++ b/src/lib/client/randomGrill.js @@ -1,7 +1,7 @@ -import { baseUrl } from "lib/client/api"; +import { CLIENT_API_URL } from "utils/config"; export async function fetchRandomGrill() { - const res = await fetch(`${baseUrl}/image?filter[facet]=Grill&sort=random&page[size]=1`); + const res = await fetch(`${CLIENT_API_URL}/image?filter[facet]=Grill&sort=random&page[size]=1`); const json = await res.json(); return json.images[0].link; diff --git a/src/lib/client/randomTheme.js b/src/lib/client/randomTheme.js index f75735c..1aaf1ac 100644 --- a/src/lib/client/randomTheme.js +++ b/src/lib/client/randomTheme.js @@ -1,10 +1,10 @@ -import { baseUrl } from "lib/client/api"; +import { CLIENT_API_URL } from "utils/config"; const backLog = []; export async function fetchRandomTheme() { if (!backLog.length) { - const res = await fetch(`${baseUrl}/animetheme?sort=random&include=anime,animethemeentries.videos&filter[has]=animethemeentries&filter[spoiler]=false`); + const res = await fetch(`${CLIENT_API_URL}/animetheme?sort=random&include=anime,animethemeentries.videos&filter[has]=animethemeentries&filter[spoiler]=false`); const json = await res.json(); backLog.push(...json.animethemes.map((theme) => { diff --git a/src/lib/client/search.js b/src/lib/client/search.js index 0c9f66d..b8942bd 100644 --- a/src/lib/client/search.js +++ b/src/lib/client/search.js @@ -1,5 +1,5 @@ -import { baseUrl } from "lib/client/api"; import { uniq } from "lodash-es"; +import { CLIENT_API_URL } from "utils/config"; const entityConfigs = { anime: { @@ -139,7 +139,7 @@ const entityConfigs = { export async function fetchGlobalSearchResults(query, limit, entities) { const parameters = generateGlobalSearchParameters(entities); - const res = await fetch(`${baseUrl}/search?${parameters.join("&")}&limit=${limit}&q=${encodeURIComponent(query)}`); + const res = await fetch(`${CLIENT_API_URL}/search?${parameters.join("&")}&limit=${limit}&q=${encodeURIComponent(query)}`); const json = await res.json(); if (!res.ok) { @@ -172,7 +172,7 @@ export async function fetchEntitySearchResults({ ...generateFilterAndSortParameters(filters, sortBy) ]; - let url = `${baseUrl}/${entityConfigs[entity].singular || entity}?${parameters.join("&")}&page[size]=${limit}&page[number]=${page}`; + let url = `${CLIENT_API_URL}/${entityConfigs[entity].singular || entity}?${parameters.join("&")}&page[size]=${limit}&page[number]=${page}`; if (query) { url += `&q=${encodeURIComponent(query)}`; } diff --git a/src/lib/common/animethemes/api.js b/src/lib/common/animethemes/api.js index 5304f4b..4b36465 100644 --- a/src/lib/common/animethemes/api.js +++ b/src/lib/common/animethemes/api.js @@ -1,10 +1,11 @@ import pLimit from "p-limit"; import { parseResolveInfo } from "graphql-parse-resolve-info"; import devLog from "utils/devLog"; +import { CLIENT_API_URL, SERVER_API_KEY, SERVER_API_URL } from "utils/config"; const limit = pLimit(5); -export const API_BASE_URL = `${process.env.ANIMETHEMES_API_URL || process.env.NEXT_PUBLIC_API_URL}`; +export const API_URL = `${SERVER_API_URL || CLIENT_API_URL}`; export const INCLUDES = { Anime: { @@ -131,7 +132,7 @@ export function apiResolver(config) { devLog.warn(`Deep fetch at: ${path}`); } - let url = `${API_BASE_URL}${endpoint(parent, args)}`; + let url = `${API_URL}${endpoint(parent, args)}`; const includes = getIncludes(info, baseInclude); if (baseInclude) { @@ -165,8 +166,19 @@ export function apiResolver(config) { return await limit(() => (async () => { if (!pagination) { - const json = await fetchJson(url); - context.apiRequests++; + let json; + + if (context?.cache?.has(url)) { + devLog.info("CACHED: " + url); + json = context.cache.get(url); + } else { + json = await fetchJson(url); + context.apiRequests++; + if (!context.cache) { + context.cache = new Map(); + } + context.cache.set(url, json); + } return transformer(extractor(json, parent, args), parent, args); } else { @@ -190,9 +202,9 @@ export function apiResolver(config) { async function fetchJson(url) { const config = {}; - if (process.env.ANIMETHEMES_API_KEY) { + if (SERVER_API_KEY) { config.headers = { - Authorization: `Bearer ${process.env.ANIMETHEMES_API_KEY}` + Authorization: `Bearer ${SERVER_API_KEY}` }; } diff --git a/src/lib/server/animebracket/brackets.json b/src/lib/server/animebracket/brackets.json new file mode 100644 index 0000000..a83f76d --- /dev/null +++ b/src/lib/server/animebracket/brackets.json @@ -0,0 +1,50 @@ +{ + "best-opening-ii": { + "slug": "best-opening", + "name": "Best Anime Opening II" + }, + "best-opening-iii": { + "slug": "best-anime-opening-1", + "name": "Best Anime Opening III" + }, + "best-opening-iv": { + "slug": "r-anime-s-best-opening-contest-", + "name": "Best Anime Opening IV" + }, + "best-opening-v": { + "slug": "best-opening-v5", + "name": "Best Anime Opening V" + }, + "best-opening-vi": { + "slug": "best-anime-opening-vi-a-salty-angel-s-thesis", + "name": "Best Anime Opening VI: A Salty Angel's Thesis" + }, + "best-opening-vii": { + "slug": "best-anime-opening-vii-3-2-1-let-s-salt-", + "name": "Best Anime Opening VII: 3, 2, 1, Let's Salt!" + }, + "best-opening-ix": { + "slug": "best-anime-opening-ix-salty-arrow", + "name": "Best Anime Opening IX: Guren na Yumiya" + }, + "best-ending-ii": { + "slug": "r-anime-s-best-ending-contest-", + "name": "Best Anime Ending II" + }, + "best-ending-iii": { + "slug": "best-ending-iii-the-salt-you-don-t-know", + "name": "Best Anime Ending III: The Salt You Don't Know" + }, + "best-ending-iv": { + "slug": "best-ending-iv-don-t-say-salty-", + "name": "Best Anime Ending IV: Don't say \"salty\"" + }, + "best-ending-v": { + "slug": "best-ending-v-na-thank-you-", + "name": "Best Anime Ending V: Na, Thank You!" + }, + "best-ending-vi": { + "slug": "best-ending-6-listen-to-salt-", + "name": "Best Anime Ending VI: Listen to Salt!!" + } +} diff --git a/src/lib/server/animebracket/mappings.json b/src/lib/server/animebracket/mappings.json index b2eb6d8..8ad0e41 100644 --- a/src/lib/server/animebracket/mappings.json +++ b/src/lib/server/animebracket/mappings.json @@ -1,258 +1,3119 @@ { - "214755": 6846, - "214730": 9335, - "215383": 4526, - "215092": 8131, - "215213": 8082, - "215355": 1796, - "215447": 9113, - "214861": 4940, - "214734": 6549, - "214896": 4207, - "214951": 7861, - "215344": 1790, - "214927": 2021, - "215176": 8471, - "215157": 7221, - "214973": 4485, - "215408": 2870, - "214907": 7239, - "214983": 2531, - "214822": 955, - "214903": 6326, - "215178": 6173, - "215634": 673, - "215544": 6878, - "215255": 7108, - "214922": 3460, - "215024": 239, - "215164": 9114, - "215538": 6237, - "215122": 5915, - "215001": 4966, - "214814": 2262, - "214969": 3888, - "214964": 4397, - "214943": 7062, - "214885": 7006, - "215209": 4663, - "215166": 9054, - "215258": 4958, - "214757": 7546, - "214953": 7562, - "214862": 3990, - "215494": 65, - "215046": 8403, - "215540": 9282, - "215033": 2057, - "215471": 6354, - "215361": 727, - "214735": 1069, - "214773": 9980, - "215174": 5424, - "215026": 9373, - "215398": 7328, - "214766": 8210, - "215076": 7138, - "214895": 8504, - "214855": 8530, - "214774": 8015, - "215186": 10072, - "214923": 7202, - "214847": 8641, - "215057": 6055, - "215270": 3699, - "214750": 7512, - "215593": 8656, - "214872": 3834, - "215019": 1479, - "215078": 3436, - "215055": 8939, - "215162": 9301, - "214759": 5930, - "215179": 2014, - "215120": 8172, - "214809": 7742, - "214882": 7009, - "214876": 7215, - "214935": 2692, - "214763": 9139, - "214934": 1126, - "215378": 1477, - "214775": 6453, - "215437": 6363, - "214834": 7357, - "215085": 3730, - "214832": 2478, - "215132": 6645, - "214932": 5519, - "214924": 6765, - "215145": 2264, - "214926": 9126, - "214867": 7678, - "215144": 3435, - "214941": 4273, - "215505": 4272, - "215177": 8175, - "215148": 6054, - "215238": 4000, - "215359": 5445, - "215264": 5687, - "214984": 10007, - "215472": 5774, - "215015": 8948, - "215521": 4664, - "215023": 3633, - "215236": 9129, - "214894": 10004, - "215020": 7936, - "215273": 6467, - "215004": 5280, - "215474": 7218, - "214915": 7015, - "214900": 9281, - "215107": 2263, - "214999": 7557, - "214836": 9278, - "215127": 6429, - "215395": 4534, - "214955": 5608, - "215212": 8125, - "214993": 8063, - "214768": 9080, - "214732": 8566, - "215219": 6457, - "214820": 4028, - "214908": 4947, - "214831": 7308, - "215154": 6095, - "215300": 10009, - "215237": 7013, - "215223": 5481, - "215208": 3362, - "215609": 5577, - "215039": 1928, - "215192": 9311, - "214978": 4511, - "214843": 8577, - "214860": 4852, - "215518": 5748, - "215139": 6114, - "215138": 8949, - "214930": 2605, - "215443": 4711, - "215093": 7872, - "215600": 8188, - "215165": 3392, - "214869": 525, - "214972": 2783, - "214958": 5116, - "215060": 9999, - "214745": 9053, - "215227": 3984, - "214795": 6663, - "214970": 7318, - "214997": 3795, - "215305": 8088, - "215111": 4973, - "214904": 5520, - "215009": 2701, - "215167": 3110, - "215116": 3981, - "215086": 8325, - "215226": 8943, - "215514": 5833, - "214956": 3361, - "215550": 8922, - "215108": 4527, - "215199": 7213, - "214886": 3164, - "215417": 7054, - "215484": 7638, - "215200": 3437, - "215232": 9987, - "214981": 4853, - "215636": 1035, - "215029": 1034, - "215022": 8787, - "215331": 2446, - "215452": 3040, - "215141": 7107, - "215007": 3438, - "215041": 6081, - "214846": 1802, - "214859": 7114, - "214744": 1654, - "215269": 2438, - "215489": 8262, - "214902": 7053, - "215419": 8083, - "215084": 3163, - "214748": 7980, - "214760": 9372, - "215480": 1794, - "214737": 2509, - "214782": 4466, - "215433": 6854, - "215486": 8940, - "214912": 6550, - "215562": 9197, - "215317": 6466, - "215027": 6633, - "215547": 7743, - "214812": 9264, - "215002": 8726, + "8489": 3434, + "8513": 3437, + "8514": 5824, + "8530": 3267, + "8533": 3265, + "8537": 3270, + "8540": 4663, + "8574": 6471, + "8625": 3273, + "8628": 5695, + "8639": 3795, + "8735": 578, + "8736": 4325, + "8737": 3436, + "8812": 5688, + "8881": 5519, + "8890": 919, + "8898": 5577, + "8899": 5576, + "8902": 2053, + "8905": 5078, + "8911": 4484, + "8912": 2093, + "8924": 2043, + "8927": 5786, + "8985": 3887, + "8986": 4526, + "8987": 6475, + "9362": 4207, + "9363": 4853, + "9364": 3040, + "9365": 4910, + "9366": 963, + "9368": 5280, + "9369": 3990, + "9371": 5608, + "9373": 4958, + "9378": 4397, + "9380": 4940, + "9381": 2692, + "9382": 3229, + "9385": 3633, + "9387": 5930, + "9389": 1653, + "9391": 5279, + "9392": 3373, + "9393": 950, + "9394": 960, + "9549": 3888, + "9550": 3163, + "9552": 2748, + "9553": 5535, + "9554": 3263, + "9698": 1801, + "9699": 1802, + "9700": 1790, + "9701": 1792, + "9702": 1791, + "9703": 5832, + "9704": 1794, + "9705": 1795, + "9706": 1803, + "9707": 1796, + "9870": 4491, + "16284": 4853, + "16287": 5798, + "16288": 4272, + "16345": 6326, + "16352": 4397, + "16353": 6403, + "16354": 3633, + "16355": 6457, + "16356": 2021, + "16357": 1437, + "16358": 2409, + "16359": 578, + "16364": 4939, + "16367": 1069, + "16368": 4234, + "16371": 6467, + "16372": 2531, + "16373": 6466, + "16374": 2692, + "16379": 4940, + "16380": 4507, + "16381": 3504, + "16383": 4910, + "16384": 5786, + "16389": 6363, + "16390": 4535, + "16393": 4133, + "16394": 5778, + "16395": 3613, + "16397": 1656, + "16398": 5771, + "16401": 5078, + "16402": 4663, + "16403": 2093, + "16404": 4259, + "16405": 3436, + "16406": 4911, + "16407": 5726, + "16408": 3834, + "16409": 1126, + "16411": 5348, + "16413": 6429, + "16414": 4829, + "16426": 3277, + "16428": 2953, + "16430": 2701, + "16432": 3267, + "16434": 5577, + "16436": 5580, + "16437": 6463, + "16438": 3041, + "16440": 6377, + "16441": 5824, + "16442": 5832, + "16444": 3438, + "16445": 6459, + "16446": 2262, + "16447": 6482, + "16448": 4484, + "16450": 5477, + "16456": 4943, + "16457": 955, + "16458": 239, + "16461": 3437, + "16463": 6509, + "16464": 5837, + "16465": 4992, + "16466": 5481, + "16467": 5826, + "16468": 5687, + "16469": 5509, + "16470": 5279, + "16471": 4534, + "16472": 4958, + "16473": 3795, + "16474": 5280, + "16476": 2840, + "16482": 4527, + "16483": 4457, + "16488": 2279, + "16489": 5695, + "16491": 4664, + "16492": 3272, + "16493": 5293, + "16494": 3163, + "16500": 3229, + "16503": 5817, + "16504": 2748, + "16505": 4485, + "16506": 4966, + "16507": 5688, + "16508": 5519, + "16509": 4880, + "16510": 3036, + "16512": 3270, + "16514": 6453, + "16515": 3376, + "16518": 5576, + "16519": 5287, + "16520": 3887, + "16522": 3990, + "16523": 4207, + "16524": 3164, + "16526": 4448, + "16527": 3981, + "16528": 4677, + "16529": 5540, + "16537": 4570, + "16539": 5418, + "16540": 3984, + "16542": 2564, + "16547": 2877, + "16551": 5315, + "16552": 3373, + "16555": 2104, + "16560": 3460, + "16563": 3361, + "16565": 3362, + "16572": 3111, + "16573": 5511, + "16575": 5117, + "16576": 2783, + "16579": 2883, + "16582": 3970, + "16583": 5772, + "16586": 4198, + "16590": 5954, + "16593": 2562, + "16594": 6493, + "16596": 523, + "16598": 673, + "16600": 4028, + "16601": 3874, + "16603": 3985, + "16604": 5556, + "16609": 3868, + "16616": 950, + "16617": 5162, + "16618": 5424, + "16621": 2057, + "16622": 2438, + "16623": 1270, + "16625": 919, + "16634": 3040, + "16645": 4526, + "16647": 5831, + "16652": 5720, + "16653": 3730, + "16656": 4224, + "16680": 2280, + "16681": 3764, + "16683": 5930, + "16686": 2875, + "16688": 10487, + "16693": 3696, + "16696": 5818, + "16700": 6354, + "16811": 6358, + "16812": 1928, + "16814": 5608, + "16818": 4212, + "16819": 4825, + "16820": 5679, + "16823": 3888, + "16824": 5587, + "16827": 6419, + "16833": 6471, + "16836": 1790, + "16837": 4815, + "16838": 4213, + "16844": 5957, + "16845": 2267, + "16846": 4211, + "16849": 5016, + "16850": 4502, + "16852": 963, + "16857": 1801, + "16859": 5535, + "16860": 5597, + "16862": 3263, + "16863": 5051, + "16867": 4970, + "16868": 3877, + "16870": 5915, + "16876": 3695, + "16877": 2014, + "16878": 2450, + "16879": 6499, + "16881": 4969, + "16883": 6475, + "16885": 5703, + "16887": 5493, + "16889": 4377, + "16895": 1792, + "16896": 2043, + "16900": 6310, + "16901": 4055, + "16904": 5916, + "16922": 2478, + "16924": 3595, + "16927": 2156, + "16928": 6514, + "16946": 3110, + "16948": 727, + "16954": 4620, + "16962": 6497, + "16966": 4714, + "16971": 2155, + "16973": 5419, + "16975": 5492, + "16977": 5288, + "16982": 6417, + "16983": 5775, + "16992": 5854, + "16998": 5583, + "17017": 4088, + "17019": 3650, + "17027": 5003, + "17032": 2873, + "17042": 1347, + "17045": 2605, + "17049": 3392, + "17054": 2509, + "17059": 3970, + "17062": 4711, + "17069": 5774, + "17087": 5208, + "17102": 5186, + "17103": 3916, + "17107": 4710, + "17108": 6342, + "17113": 5071, + "17114": 4029, + "17115": 4832, + "17116": 4682, + "17117": 4196, + "17118": 3915, + "17120": 5520, + "17130": 5072, + "17137": 5881, + "17141": 4973, + "17142": 3431, + "17153": 6539, + "17155": 2446, + "17159": 5455, + "17166": 5445, + "17169": 5248, + "17171": 753, + "17177": 6412, + "17179": 1034, + "17181": 3929, + "17195": 4947, + "17210": 1563, + "17215": 2928, + "17218": 951, + "17226": 1794, + "50820": 578, + "50824": 2279, + "50825": 1654, + "50827": 5481, + "50828": 523, + "50829": 919, + "50830": 3164, + "50831": 2409, + "50832": 3163, + "50833": 5535, + "50834": 6354, + "50835": 6095, + "50836": 4710, + "50838": 5117, + "50839": 2783, + "50840": 2531, + "50842": 1141, + "50847": 4485, + "50848": 2446, + "50849": 4466, + "50851": 4235, + "50853": 4970, + "50854": 2871, + "50855": 6336, + "50857": 2870, + "50858": 3868, + "50860": 5519, + "50862": 1656, + "50865": 6055, + "50866": 4272, + "50867": 4958, + "50868": 4526, + "50869": 5774, + "50873": 6139, + "50877": 6457, + "50880": 6113, + "50881": 4940, + "50883": 3990, + "50887": 6471, + "50889": 5930, + "50890": 3437, + "50893": 5511, + "50896": 6114, + "50898": 3990, + "50899": 2478, + "50900": 6081, + "50901": 2692, + "50902": 6244, + "50903": 3984, + "50904": 4711, + "50907": 1069, + "50917": 6206, + "50921": 6097, + "50922": 2043, + "50924": 5280, + "50926": 6453, + "50927": 2264, + "50929": 5561, + "50930": 6201, + "50932": 5817, + "50934": 3887, + "50935": 5279, + "50936": 6475, + "50939": 3613, + "50943": 4397, + "50945": 1437, + "50946": 5577, + "50947": 6121, + "50949": 4853, + "50950": 3229, + "50953": 6429, + "50955": 6230, + "50964": 3361, + "50970": 3730, + "50971": 4259, + "50972": 5556, + "50973": 3376, + "50977": 2562, + "50978": 4484, + "50980": 4939, + "50982": 1794, + "50988": 4207, + "50989": 239, + "50990": 2840, + "50993": 5078, + "51001": 4507, + "51003": 5832, + "51004": 6430, + "51006": 1126, + "51009": 1795, + "51018": 3985, + "51020": 6514, + "51025": 5915, + "51027": 5424, + "51029": 6310, + "51030": 6363, + "51032": 5837, + "51033": 6403, + "51035": 5786, + "51036": 7114, + "51039": 6239, + "51041": 1034, + "51045": 2021, + "51048": 5679, + "51054": 4088, + "51056": 3696, + "51060": 5162, + "51065": 3373, + "51066": 3036, + "51068": 6210, + "51069": 1928, + "51070": 5293, + "51071": 3915, + "51077": 5687, + "51084": 6054, + "51086": 5688, + "51101": 3888, + "51102": 3795, + "51112": 5824, + "51114": 2605, + "51116": 2564, + "51119": 4078, + "51120": 6237, + "51126": 5520, + "51127": 4702, + "51129": 6326, + "51133": 4947, + "51143": 3040, + "51144": 6417, + "51145": 3460, + "51148": 4663, + "51149": 3981, + "51155": 6243, + "51157": 2953, + "51162": 4234, + "51166": 7127, + "51170": 5576, + "51172": 1802, + "51174": 6173, + "51175": 5775, + "51176": 4534, + "51177": 5587, + "51178": 7123, + "51184": 5492, + "51187": 5580, + "51189": 2873, + "51198": 2450, + "51202": 3435, + "51206": 4213, + "51209": 4224, + "51210": 6335, + "51214": 2438, + "51217": 4973, + "51220": 3110, + "51227": 5541, + "51231": 5608, + "51236": 5445, + "51243": 1479, + "51249": 5116, + "51254": 3267, + "51270": 5703, + "51273": 7155, + "51276": 4448, + "51278": 6334, + "51280": 1655, + "51281": 2868, + "51282": 4198, + "51283": 3834, + "51284": 3695, + "51285": 4029, + "51286": 4457, + "51287": 5748, + "51288": 10487, + "51293": 2453, + "51306": 5287, + "51307": 3377, + "51312": 6214, + "51315": 6116, + "51317": 1792, + "51327": 6279, + "51341": 753, + "51348": 4520, + "51349": 3392, + "51354": 6493, + "51359": 2093, + "51360": 3041, + "51366": 3169, + "51373": 4721, + "51375": 7107, + "51377": 1477, + "51378": 950, + "51384": 4233, + "51386": 4273, + "51398": 2701, + "51399": 3375, + "51403": 7119, + "51408": 5115, + "51411": 3436, + "51414": 4028, + "51416": 3835, + "51418": 4612, + "51421": 5124, + "51422": 6507, + "51423": 3270, + "51435": 5121, + "51437": 7131, + "51456": 4527, + "51457": 2748, + "51461": 6412, + "51465": 3362, + "51468": 5540, + "51487": 1517, + "51489": 1790, + "51495": 5208, + "51497": 4664, + "51498": 4511, + "51548": 3764, + "51551": 4852, + "51554": 5818, + "51565": 4829, + "51568": 3456, + "51569": 1801, + "51570": 2280, + "51578": 6463, + "51584": 5493, + "51590": 3374, + "51591": 5831, + "51592": 3438, + "51593": 3111, + "51611": 5616, + "51614": 4851, + "51615": 1653, + "51619": 5825, + "51622": 4832, + "51631": 6196, + "51632": 6466, + "51641": 4966, + "51642": 5879, + "51647": 4910, + "51649": 6482, + "51666": 5916, + "51689": 6232, + "51692": 4550, + "51696": 2262, + "51697": 5248, + "51698": 2448, + "51703": 3504, + "51712": 6333, + "51717": 4677, + "51723": 6467, + "51728": 4211, + "51732": 717, + "51746": 5125, + "51754": 7138, + "51756": 4535, + "51758": 3929, + "67192": 3043, + "67193": 6431, + "67194": 5787, + "67196": 4486, + "67197": 3378, + "67198": 2458, + "67203": 3042, + "67204": 4399, + "67206": 4831, + "67207": 6420, + "67209": 5704, + "67211": 5827, + "67213": 4214, + "67217": 4326, + "67218": 6174, + "67223": 2022, + "67224": 4197, + "67226": 2468, + "67227": 5282, + "67228": 5521, + "67229": 3993, + "67230": 5163, + "67231": 7132, + "67232": 3992, + "67234": 1022, + "67237": 6238, + "67240": 4967, + "67244": 4237, + "67246": 6231, + "67247": 5922, + "67248": 6203, + "67249": 6337, + "67299": 5512, + "67300": 6540, + "67302": 6047, + "67303": 3167, + "67304": 5579, + "67305": 1821, + "67306": 6056, + "67308": 755, + "67310": 4001, + "67311": 3442, + "67312": 3991, + "67313": 1070, + "67317": 3396, + "67322": 2606, + "67325": 4537, + "67328": 4611, + "67331": 3799, + "67333": 7115, + "67334": 4941, + "67341": 1809, + "67345": 4528, + "67346": 5721, + "67350": 2954, + "67351": 1216, + "67352": 6515, + "67353": 6233, + "67354": 5834, + "67355": 7120, + "67439": 1258, + "67459": 4239, + "67460": 5542, + "67461": 1658, + "67462": 4378, + "67463": 6472, + "67465": 4713, + "67470": 4971, + "67471": 6038, + "67472": 6458, + "67473": 4030, + "67474": 2916, + "67475": 4199, + "67476": 4209, + "67479": 6329, + "67481": 4961, + "67488": 6140, + "67489": 3443, + "67493": 6084, + "67501": 6285, + "67502": 5349, + "67503": 3982, + "67504": 3286, + "67505": 4472, + "67511": 3987, + "67512": 2461, + "67514": 5582, + "67515": 4948, + "67517": 6476, + "67518": 5289, + "67519": 579, + "67522": 2462, + "67525": 7142, + "67526": 6096, + "67527": 3440, + "67528": 5931, + "67529": 3365, + "67530": 4238, + "67531": 6338, + "67532": 6098, + "67533": 4680, + "67541": 3876, + "67543": 4400, + "67545": 1334, + "67596": 6355, + "67597": 5019, + "67601": 3836, + "67602": 3797, + "67604": 4529, + "67605": 5777, + "67607": 3698, + "67608": 5690, + "67610": 5692, + "67611": 2466, + "67614": 5421, + "67616": 6432, + "67617": 3995, + "67618": 4552, + "67620": 4613, + "67621": 2266, + "67623": 2843, + "67625": 6494, + "67627": 1832, + "67628": 236, + "67629": 4624, + "67630": 6099, + "67631": 2411, + "67632": 3441, + "67638": 4959, + "67639": 5055, + "67643": 5691, + "67644": 5588, + "67646": 5609, + "67648": 4666, + "67650": 5482, + "67652": 6339, + "67654": 6364, + "67656": 4510, + "67657": 5052, + "67658": 7124, + "67663": 4090, + "67665": 2841, + "67667": 2015, + "67671": 4912, + "67672": 7128, + "67677": 4854, + "67678": 5880, + "67679": 973, + "67680": 2459, + "67682": 5067, + "67689": 3363, + "67691": 2281, + "67692": 5494, + "67695": 729, + "67696": 4972, + "67701": 4469, + "67703": 2693, + "68153": 4281, + "68154": 2510, + "68156": 2456, + "68160": 1657, + "68161": 580, + "68162": 6327, + "68163": 2467, + "68164": 5391, + "68165": 6204, + "68172": 6117, + "68173": 2454, + "68174": 2455, + "68175": 3251, + "68179": 5350, + "68183": 2460, + "68184": 6340, + "68231": 2532, + "68236": 3289, + "68237": 5838, + "68239": 6197, + "68241": 5819, + "68246": 4521, + "68247": 5446, + "68248": 4458, + "68250": 3165, + "68251": 1438, + "68252": 5543, + "68254": 6483, + "68257": 2888, + "68267": 6468, + "68268": 3917, + "68269": 2463, + "68270": 4623, + "68271": 7104, + "68273": 3837, + "68274": 6413, + "68275": 4079, + "68276": 1833, + "68281": 6057, + "68283": 3283, + "68286": 3166, + "68288": 4467, + "68295": 581, + "68296": 5696, + "68297": 3926, + "68304": 2469, + "68306": 6211, + "68308": 2568, + "68309": 920, + "68310": 5447, + "68311": 2479, + "68315": 1805, + "68320": 3505, + "68325": 3461, + "68326": 2457, + "68330": 3230, + "68334": 6156, + "68337": 4704, + "68344": 5294, + "68345": 5617, + "68346": 3168, + "68348": 3383, + "68350": 5425, + "68355": 5118, + "68358": 5235, + "68360": 4833, + "68365": 5562, + "68368": 2465, + "68369": 2750, + "68372": 4942, + "68373": 5958, + "68374": 5281, + "68376": 6247, + "68379": 6454, + "68382": 3393, + "68386": 756, + "68388": 5390, + "68391": 5164, + "68392": 2282, + "68394": 4974, + "68395": 3765, + "68397": 2464, + "68398": 3439, + "68402": 3620, + "68403": 2996, + "68405": 5672, + "68407": 5581, + "68408": 5495, + "68411": 3889, + "68414": 4236, + "68415": 10488, + "68416": 3280, + "68424": 4679, + "68426": 5776, + "68432": 3919, + "68433": 4622, + "68437": 3890, + "81772": 6363, + "81773": 3874, + "81775": 1479, + "81776": 5703, + "81779": 3504, + "81781": 6114, + "81782": 5786, + "81784": 4527, + "81786": 2715, + "81788": 4273, + "81789": 5519, + "81792": 1069, + "81794": 3956, + "81796": 2783, + "81802": 5535, + "81804": 4028, + "81805": 2883, + "81806": 7107, + "81807": 4663, + "81809": 7114, + "81813": 7195, + "81814": 2262, + "81815": 6457, + "81817": 3163, + "81819": 3835, + "81823": 2692, + "81824": 2840, + "81825": 4397, + "81828": 6244, + "81829": 5115, + "81831": 3613, + "81833": 4526, + "81838": 3375, + "81839": 2953, + "81841": 6173, + "81843": 6333, + "81845": 4853, + "81846": 1928, + "81847": 2438, + "81850": 1802, + "81854": 4940, + "81855": 6095, + "81861": 3981, + "81865": 4520, + "81866": 6403, + "81867": 4485, + "81868": 4958, + "81869": 2450, + "81870": 5832, + "81871": 6230, + "81874": 5774, + "81877": 4610, + "81878": 5561, + "81879": 4259, + "81884": 1790, + "81886": 6139, + "81887": 919, + "81888": 4534, + "81893": 5930, + "81894": 4702, + "81895": 2562, + "81898": 5576, + "81902": 3984, + "81903": 5837, + "81904": 3409, + "81905": 6081, + "81909": 2409, + "81917": 4233, + "81919": 7108, + "81920": 6055, + "81922": 5124, + "81925": 3361, + "81927": 3437, + "81929": 5608, + "81932": 6336, + "81935": 1126, + "81936": 4088, + "81941": 6054, + "81942": 6466, + "81943": 6237, + "81945": 4970, + "81947": 6326, + "81952": 5117, + "81954": 3795, + "81955": 2871, + "81956": 5481, + "81958": 3435, + "81959": 239, + "81960": 5916, + "81963": 3887, + "81964": 4466, + "81965": 6116, + "81967": 5162, + "81968": 3040, + "81969": 1437, + "81973": 5720, + "81976": 523, + "81978": 578, + "81979": 4939, + "81981": 2279, + "81983": 3376, + "81985": 1795, + "81986": 5687, + "81988": 7131, + "81989": 4851, + "81991": 1792, + "81992": 2451, + "81993": 6429, + "81995": 6097, + "81996": 4484, + "81997": 7138, + "81999": 2280, + "82002": 6113, + "82005": 3915, + "82006": 4207, + "82008": 1794, + "82009": 2478, + "82011": 4133, + "82012": 3985, + "82014": 3373, + "82015": 950, + "82017": 6354, + "82018": 5556, + "82019": 3438, + "82020": 3460, + "82025": 6239, + "82026": 6482, + "82027": 727, + "82029": 5915, + "82030": 4711, + "82033": 5587, + "82037": 5520, + "82038": 6232, + "82039": 6201, + "82040": 3916, + "82041": 2446, + "82042": 2021, + "82043": 4973, + "82047": 3990, + "82051": 2531, + "82054": 5287, + "82056": 5279, + "82059": 5445, + "82064": 6334, + "82073": 3888, + "82074": 2178, + "82075": 3164, + "82080": 3229, + "82086": 2564, + "82089": 4721, + "82090": 2264, + "82091": 3041, + "82093": 2470, + "82094": 5616, + "82095": 4457, + "82099": 4947, + "82101": 5121, + "82103": 5541, + "82105": 5493, + "82108": 5817, + "82110": 5540, + "82114": 6335, + "82115": 6514, + "82116": 6475, + "82119": 6206, + "82126": 5511, + "82129": 4815, + "82132": 6310, + "82134": 7123, + "82136": 4224, + "82137": 7127, + "82142": 1801, + "82148": 5424, + "82151": 3110, + "82152": 2868, + "82154": 6417, + "82159": 4710, + "82160": 2875, + "82165": 963, + "82168": 3392, + "82171": 6121, + "82174": 3633, + "82179": 6210, + "82181": 2471, + "82182": 5679, + "82184": 5389, + "82185": 2748, + "82222": 5280, + "82224": 1654, + "82230": 717, + "82231": 2453, + "82263": 3377, + "82274": 3436, + "82282": 3362, + "82284": 5688, + "82285": 2043, + "82286": 4910, + "82287": 6243, + "82289": 1656, + "82290": 3036, + "82293": 5580, + "82298": 5577, + "82299": 2093, + "82302": 2565, + "82303": 4852, + "82308": 4272, + "82313": 3868, + "82317": 3983, + "82318": 2605, + "82319": 3695, + "82320": 2509, + "82321": 3374, + "82323": 3111, + "82326": 5078, + "82327": 4029, + "82329": 6453, + "82330": 5293, + "82332": 4966, + "82333": 4000, + "82334": 753, + "82335": 673, + "82339": 1785, + "82341": 5116, + "82344": 1034, + "82349": 962, + "82366": 3730, + "82369": 5492, + "82370": 3267, + "82376": 3834, + "82378": 1477, + "82387": 4550, + "82393": 3929, + "82401": 4078, + "82404": 6115, + "82407": 6463, + "82412": 4535, + "82415": 6196, + "82428": 6430, + "82431": 7155, + "82433": 5798, + "82435": 4621, + "82444": 5125, + "82464": 5045, + "82469": 6545, + "82471": 5348, + "82472": 2263, + "82488": 5957, + "82492": 4448, + "82494": 5288, + "82496": 4511, + "82513": 2873, + "82548": 4211, + "82575": 7119, + "82589": 6279, + "82600": 5749, + "82634": 5248, + "92595": 6047, + "92596": 4458, + "92597": 3461, + "92598": 7845, + "92599": 3442, + "92600": 3982, + "92601": 5834, + "92602": 4948, + "92603": 3439, + "92604": 6847, + "92605": 5390, + "92606": 2532, + "92609": 3383, + "92610": 4703, + "92611": 7094, + "92612": 3617, + "92614": 6973, + "92617": 2022, + "92618": 4967, + "92619": 2841, + "92620": 7864, + "92621": 920, + "92622": 3992, + "92625": 579, + "92628": 7016, + "92629": 7055, + "92630": 7870, + "92631": 4326, + "92632": 3440, + "92633": 3836, + "92639": 4399, + "92640": 3876, + "92641": 5787, + "92642": 7855, + "92643": 6454, + "92644": 6472, + "92647": 4611, + "92648": 1438, + "92649": 3837, + "92652": 3070, + "92653": 6553, + "92654": 5838, + "92655": 5521, + "92656": 4328, + "92660": 5294, + "92661": 6056, + "92662": 4209, + "92663": 6083, + "92664": 6634, + "92668": 7057, + "92669": 5581, + "92672": 4941, + "92674": 7139, + "92675": 7916, + "92676": 6355, + "92680": 7115, + "92681": 6664, + "92682": 6431, + "92683": 3042, + "92684": 7862, + "92685": 6468, + "92686": 6821, + "92687": 6364, + "92691": 6540, + "92695": 5282, + "92696": 3995, + "92698": 4486, + "92700": 4537, + "92701": 4398, + "92704": 5588, + "92708": 2266, + "92711": 4236, + "92713": 4831, + "92715": 5494, + "92717": 1485, + "92733": 7937, + "92736": 5691, + "92738": 6432, + "92739": 5281, + "92743": 2282, + "92745": 7938, + "92746": 2606, + "92748": 2281, + "92756": 4528, + "92763": 4001, + "92768": 2852, + "92771": 4274, + "92772": 4529, + "92773": 4514, + "92776": 6327, + "92778": 581, + "92781": 3441, + "92788": 3797, + "92790": 6483, + "92792": 5235, + "92800": 4400, + "92804": 6856, + "92807": 1216, + "92811": 4971, + "92813": 2908, + "92815": 4972, + "92817": 5074, + "92818": 4942, + "92820": 5482, + "92821": 2458, + "92822": 5827, + "92825": 3991, + "92826": 3035, + "92828": 6551, + "92829": 3889, + "92830": 5512, + "92832": 3511, + "92833": 4713, + "92838": 5777, + "92840": 5704, + "92841": 7120, + "92846": 3994, + "92847": 1258, + "92857": 6038, + "92859": 4552, + "92861": 5582, + "92866": 5692, + "92874": 6174, + "92877": 5696, + "92878": 2455, + "92881": 4854, + "92884": 7109, + "92885": 3651, + "92888": 5913, + "92889": 6476, + "92890": 7086, + "92891": 2631, + "92894": 2265, + "92900": 3987, + "92902": 3043, + "92903": 3365, + "92906": 7837, + "92919": 4030, + "92920": 5425, + "92921": 5391, + "92925": 2563, + "92934": 5931, + "92938": 7132, + "92942": 5522, + "92948": 4833, + "92951": 3113, + "92957": 3993, + "92960": 6238, + "92966": 4959, + "92967": 3363, + "92970": 2843, + "92975": 5776, + "92979": 5820, + "92991": 6340, + "93007": 4510, + "93008": 2026, + "93015": 6231, + "93020": 7128, + "93026": 4665, + "93027": 4712, + "93028": 936, + "93036": 1805, + "93041": 6418, + "93046": 4704, + "93048": 6912, + "93049": 2256, + "93054": 7017, + "93058": 5578, + "93060": 2693, + "93063": 5350, + "93074": 4538, + "93075": 3505, + "93076": 6057, + "93078": 5446, + "93080": 6225, + "93088": 5542, + "93094": 6233, + "93097": 5017, + "93105": 6651, + "93110": 3393, + "93113": 3165, + "93114": 7846, + "93120": 5290, + "93121": 2044, + "93129": 7010, + "93131": 3112, + "93135": 6197, + "93137": 2842, + "93143": 7014, + "93154": 4666, + "93159": 3890, + "93161": 3443, + "93163": 2023, + "93171": 6766, + "93175": 6628, + "93180": 3959, + "93190": 3634, + "93225": 6987, + "93227": 2700, + "93241": 5921, + "93249": 5609, + "93257": 6882, + "93264": 3799, + "93266": 5579, + "93270": 7873, + "93276": 4501, + "93285": 3230, + "93287": 5284, + "93289": 7110, + "93308": 974, + "93319": 5118, + "93321": 6175, + "93323": 4974, + "93325": 6117, + "93327": 4622, + "93342": 5919, + "93345": 3168, + "93346": 5484, + "93355": 6098, + "93368": 6515, + "93413": 6096, + "93420": 2025, + "93427": 1327, + "93432": 5680, + "93449": 6405, + "93450": 5562, + "93454": 5543, + "93460": 1809, + "93524": 4031, + "93544": 4281, + "93549": 5209, + "93550": 1807, + "93559": 6406, + "93593": 5239, + "93606": 2024, + "93613": 6484, + "93636": 6659, + "93641": 5440, + "93642": 5880, + "93648": 3930, + "93657": 5120, + "93659": 6311, + "93665": 6880, + "93679": 5922, + "93692": 6204, + "93710": 5544, + "93713": 5289, + "93726": 6859, + "93763": 580, + "93767": 2954, + "93781": 3986, + "93808": 5690, + "93810": 7059, + "104802": 6549, + "104803": 3438, + "104808": 7758, + "104811": 6550, + "104812": 7809, + "104814": 4958, + "104816": 2014, + "104817": 1785, + "104818": 7915, + "104819": 5279, + "104822": 2263, + "104823": 7871, + "104826": 1437, + "104830": 3835, + "104831": 6650, + "104832": 2840, + "104834": 7009, + "104837": 1034, + "104842": 7863, + "104845": 2478, + "104847": 6354, + "104848": 6627, + "104850": 6326, + "104852": 1270, + "104856": 5837, + "104862": 7773, + "104867": 4088, + "104873": 2409, + "104874": 5280, + "104875": 6336, + "104878": 7131, + "104882": 5481, + "104883": 2446, + "104884": 5287, + "104885": 4853, + "104886": 2262, + "104887": 4940, + "104888": 578, + "104889": 5288, + "104891": 7108, + "104892": 7638, + "104896": 3229, + "104898": 3361, + "104900": 4235, + "104901": 3437, + "104902": 2953, + "104911": 7107, + "104912": 4466, + "104913": 3362, + "104914": 2531, + "104916": 3984, + "104917": 6113, + "104920": 6363, + "104921": 4663, + "104923": 4397, + "104929": 3613, + "104930": 4484, + "104931": 4710, + "104932": 4448, + "104933": 5687, + "104934": 6097, + "104935": 4664, + "104936": 1126, + "104937": 7872, + "104938": 5832, + "104939": 2870, + "104941": 7053, + "104942": 6430, + "104945": 5519, + "104946": 7742, + "104948": 6658, + "104951": 7678, + "104953": 2873, + "104954": 6911, + "104957": 7114, + "104958": 3981, + "104959": 2178, + "104962": 2021, + "104965": 3376, + "104966": 3874, + "104967": 6429, + "104968": 6081, + "104969": 5688, + "104970": 4711, + "104973": 3164, + "104977": 5817, + "104978": 7936, + "104979": 2783, + "104980": 5576, + "104984": 5520, + "104985": 4969, + "104986": 7138, + "104987": 6467, + "104994": 7861, + "105005": 6237, + "105006": 4207, + "105011": 6846, + "105012": 3985, + "105013": 4527, + "105016": 5824, + "105019": 6457, + "105020": 6095, + "105021": 3111, + "105022": 2438, + "105024": 2692, + "105026": 5786, + "105030": 4966, + "105032": 4273, + "105037": 7195, + "105038": 1069, + "105039": 5162, + "105051": 4973, + "105057": 3040, + "105058": 4702, + "105065": 5587, + "105071": 1515, + "105075": 5293, + "105078": 7015, + "105081": 4224, + "105082": 3795, + "105084": 7062, + "105085": 6663, + "105086": 6633, + "105087": 4911, + "105089": 6972, + "105093": 5679, + "105099": 4028, + "105103": 6055, + "105107": 6765, + "105111": 239, + "105112": 955, + "105113": 4910, + "105114": 4272, + "105115": 1035, + "105120": 6645, + "105123": 5703, + "105128": 6453, + "105134": 6769, + "105137": 5775, + "105139": 4939, + "105142": 2280, + "105143": 2748, + "105146": 727, + "105147": 523, + "105148": 7776, + "105152": 2279, + "105153": 7054, + "105154": 3990, + "105156": 2509, + "105159": 552, + "105160": 6118, + "105163": 1479, + "105164": 5577, + "105166": 5930, + "105167": 7869, + "105169": 2564, + "105186": 7013, + "105187": 6201, + "105189": 7123, + "105192": 4526, + "105201": 4212, + "105204": 5248, + "105205": 3633, + "105217": 5424, + "105221": 5608, + "105222": 3435, + "105224": 6584, + "105233": 673, + "105235": 4947, + "105238": 6475, + "105243": 6173, + "105256": 3916, + "105261": 4534, + "105268": 6482, + "105277": 3504, + "105279": 5580, + "105281": 4677, + "105282": 3374, + "105287": 7836, + "105294": 950, + "105296": 3037, + "105307": 3373, + "105309": 5078, + "105310": 5511, + "105312": 5774, + "105314": 2883, + "105315": 3436, + "105318": 3041, + "105324": 3834, + "105325": 1928, + "105338": 3460, + "105340": 1790, + "105349": 4198, + "105355": 6854, + "105361": 1794, + "105375": 717, + "105385": 1804, + "105387": 6230, + "105388": 2605, + "105390": 4259, + "105391": 3730, + "105392": 3163, + "105397": 1802, + "105400": 1801, + "105403": 1656, + "105404": 3929, + "105405": 4536, + "105411": 4511, + "105422": 1477, + "105447": 7844, + "105448": 3887, + "105456": 4535, + "105457": 5445, + "105461": 4457, + "105473": 4000, + "105482": 6820, + "105483": 6514, + "105499": 4829, + "105507": 964, + "105514": 5540, + "105518": 7127, + "105520": 3888, + "105522": 6054, + "105523": 6196, + "105525": 6878, + "105528": 3431, + "105529": 7093, + "105530": 676, + "105537": 5879, + "105540": 4485, + "105542": 10956, + "105544": 5283, + "105577": 967, + "105601": 5916, + "105602": 6403, + "105603": 5915, + "105631": 6975, + "105650": 2093, + "105654": 6184, + "105659": 6310, + "105661": 3265, + "105746": 7789, + "105749": 4621, + "105784": 3392, + "105804": 4213, + "105839": 5720, + "105856": 5818, + "105858": 2749, + "105888": 4721, + "105919": 6239, + "125280": 5521, + "125282": 6364, + "125283": 6551, + "125290": 4529, + "125291": 5582, + "125297": 7016, + "125302": 4528, + "125303": 2902, + "125306": 7874, + "125312": 6468, + "125314": 7790, + "125316": 5931, + "125317": 4941, + "125318": 2563, + "125320": 7870, + "125326": 5512, + "125331": 4713, + "125334": 7679, + "125336": 2266, + "125338": 7641, + "125343": 4236, + "125344": 5680, + "125345": 5787, + "125346": 3439, + "125347": 3443, + "125352": 2788, + "125355": 579, + "125358": 4274, + "125362": 5838, + "125363": 3132, + "125367": 5209, + "125368": 6038, + "125369": 2927, + "125374": 7365, + "125376": 6976, + "125377": 7864, + "125378": 7094, + "125382": 7640, + "125383": 7837, + "125384": 1216, + "125385": 2843, + "125386": 3995, + "125389": 3651, + "125392": 2841, + "125394": 1334, + "125395": 1485, + "125397": 4486, + "125399": 7309, + "125400": 6515, + "125402": 4399, + "125403": 7139, + "125404": 4703, + "125405": 3042, + "125407": 7055, + "125408": 6057, + "125411": 2532, + "125671": 4209, + "125674": 6973, + "125677": 6483, + "125679": 7845, + "125680": 2920, + "125684": 7010, + "125786": 7014, + "125787": 6472, + "125833": 3836, + "125834": 6634, + "125835": 3441, + "125840": 4666, + "125842": 4326, + "125847": 6821, + "125849": 7547, + "125850": 4948, + "125851": 6847, + "125854": 4665, + "125856": 3442, + "125857": 6553, + "125865": 5588, + "125869": 2022, + "125872": 4613, + "125876": 3992, + "125879": 6540, + "125880": 2479, + "125881": 3230, + "125883": 3837, + "125885": 7760, + "125886": 7563, + "125887": 1127, + "125898": 4704, + "125901": 3994, + "125902": 5834, + "125903": 2693, + "125904": 5294, + "125905": 7937, + "125908": 3505, + "125912": 6431, + "125913": 7115, + "125914": 4831, + "125919": 5696, + "125921": 7513, + "125926": 3168, + "125927": 7391, + "125928": 6096, + "125930": 6405, + "125931": 5350, + "125932": 4400, + "125933": 4959, + "125935": 6238, + "125936": 7329, + "125941": 3166, + "125942": 5281, + "125943": 6311, + "125947": 6646, + "125948": 7774, + "125950": 2411, + "125951": 3044, + "125952": 3889, + "125954": 7862, + "125956": 5704, + "125957": 3986, + "125961": 4854, + "125964": 3982, + "125972": 7777, + "126000": 6766, + "126008": 920, + "126010": 3165, + "126013": 3167, + "126019": 3890, + "126021": 6987, + "126022": 1807, + "126023": 5692, + "126026": 4942, + "126027": 3280, + "126028": 7240, + "126029": 3043, + "126034": 6355, + "126039": 6476, + "126042": 2265, + "126044": 3440, + "126046": 7589, + "126053": 7057, + "126054": 5120, + "126058": 3765, + "126065": 5482, + "126078": 6664, + "126081": 2455, + "126088": 5543, + "126090": 5282, + "126092": 4328, + "126104": 6454, + "126107": 3799, + "126108": 6856, + "126111": 2281, + "126115": 4030, + "126120": 5880, + "126123": 6830, + "126124": 5446, + "126125": 4712, + "126129": 581, + "126131": 4537, + "126134": 1070, + "126135": 5777, + "126136": 2282, + "126137": 6585, + "126145": 5922, + "126149": 3993, + "126155": 7109, + "126156": 7873, + "126158": 7613, + "126165": 6197, + "126171": 4912, + "126176": 5119, + "126177": 4398, + "126179": 6225, + "126182": 7120, + "126194": 3797, + "126196": 6912, + "126201": 6056, + "126204": 4458, + "126205": 6047, + "126207": 3393, + "126210": 3876, + "126219": 6233, + "126220": 7580, + "126231": 7204, + "126237": 5776, + "126244": 7060, + "126248": 580, + "126253": 1488, + "126257": 3991, + "126260": 5799, + "126261": 6098, + "126268": 6651, + "126269": 4031, + "126276": 7582, + "126277": 7110, + "126280": 3070, + "126282": 7359, + "126289": 5284, + "126290": 2458, + "126291": 6327, + "126292": 4974, + "126293": 4552, + "126294": 7744, + "126302": 5484, + "126305": 7310, + "126307": 3365, + "126314": 2606, + "126336": 4001, + "126368": 7846, + "126389": 3113, + "126399": 3112, + "126401": 7394, + "126404": 5163, + "126406": 5365, + "126418": 5425, + "126465": 674, + "126484": 5863, + "126485": 5390, + "126495": 4472, + "126497": 5542, + "126514": 3987, + "126515": 1327, + "126517": 3396, + "126520": 7132, + "126528": 6888, + "126536": 2852, + "126539": 6432, + "126541": 3511, + "126545": 905, + "126547": 4624, + "126557": 7746, + "126562": 6174, + "126567": 7059, + "126568": 5820, + "126570": 3959, + "126582": 4833, + "126583": 5609, + "126585": 4680, + "126586": 5391, + "126599": 6824, + "126601": 6659, + "126606": 7612, + "126609": 5913, + "126612": 5544, + "126615": 6337, + "126617": 5494, + "126625": 7565, + "126628": 5690, + "126630": 5118, + "126637": 6484, + "126641": 3363, + "126647": 3038, + "126652": 7056, + "126699": 7393, + "143050": 2870, + "143052": 7006, + "143053": 7562, + "143057": 1126, + "143065": 7239, + "143067": 5930, + "143069": 7512, + "143074": 4485, + "143078": 5279, + "143082": 7108, + "143083": 7773, + "143087": 3435, + "143088": 6972, + "143089": 2262, + "143092": 3976, + "143093": 6769, + "143094": 7054, + "143096": 4853, + "143098": 5580, + "143100": 3438, + "143103": 5915, + "143105": 7392, + "143106": 950, + "143110": 2279, + "143111": 2478, + "143117": 4534, + "143120": 3375, + "143121": 4028, + "143122": 8726, + "143124": 4664, + "143127": 5576, + "143128": 3460, + "143133": 6453, + "143139": 3373, + "143144": 4958, + "143145": 8566, + "143146": 7638, + "143156": 7318, + "143158": 2783, + "143160": 4213, + "143163": 4940, + "143166": 3229, + "143167": 3795, + "143168": 6081, + "143171": 1035, + "143172": 3362, + "143173": 5817, + "143176": 1785, + "143177": 5162, + "143182": 2873, + "143183": 5511, + "143185": 4832, + "143194": 1928, + "143195": 7861, + "143197": 4484, + "143198": 2953, + "143200": 727, + "143203": 1479, + "143204": 1257, + "143205": 2883, + "143206": 6820, + "143209": 7809, + "143210": 6467, + "143211": 5520, + "143213": 1515, + "143214": 7579, + "143215": 7789, + "143219": 5519, + "143220": 8719, + "143222": 5280, + "143223": 673, + "143225": 2840, + "143226": 5798, + "143228": 4457, + "143230": 1034, + "143232": 3437, + "143233": 3984, + "143234": 6466, + "143237": 6550, + "143239": 7015, + "143240": 3990, + "143245": 6363, + "143246": 5688, + "143247": 7328, + "143250": 1656, + "143251": 5832, + "143252": 2564, + "143254": 4710, + "143256": 4207, + "143257": 7588, + "143258": 7107, + "143259": 7062, + "143261": 3111, + "143262": 7357, + "143263": 4711, + "143267": 6173, + "143268": 6633, + "143270": 6457, + "143272": 1069, + "143273": 5293, + "143274": 2531, + "143276": 4527, + "143277": 8641, + "143278": 4663, + "143279": 5577, + "143284": 7131, + "143285": 1802, + "143286": 2409, + "143289": 8705, + "143290": 7678, + "143292": 3361, + "143293": 7308, + "143294": 5837, + "143295": 3436, + "143297": 3110, + "143298": 5916, + "143300": 4272, + "143301": 3040, + "143302": 6095, + "143303": 3981, + "143307": 3376, + "143310": 4196, + "143312": 239, + "143316": 7114, + "143323": 6846, + "143324": 5786, + "143325": 3834, + "143327": 5687, + "143328": 7639, + "143330": 7869, + "143335": 7009, + "143336": 2021, + "143338": 7557, + "143339": 3163, + "143340": 8629, + "143343": 7546, + "143346": 6765, + "143348": 8577, + "143350": 964, + "143351": 8664, + "143352": 4397, + "143353": 3835, + "143354": 4702, + "143356": 7053, + "143360": 6326, + "143361": 6354, + "143363": 1477, + "143365": 3985, + "143368": 2280, + "143372": 5679, + "143376": 1794, + "143378": 6237, + "143383": 1147, + "143389": 6097, + "143390": 3874, + "143396": 4273, + "143401": 7915, + "143402": 5540, + "143410": 3504, + "143412": 2452, + "143415": 6055, + "143416": 5424, + "143419": 3041, + "143420": 7203, + "143422": 5608, + "143424": 6310, + "143425": 6650, + "143426": 552, + "143429": 7863, + "143433": 4535, + "143437": 6878, + "143440": 6854, + "143444": 3874, + "143445": 1790, + "143448": 4526, + "143452": 8656, + "143454": 7872, + "143458": 5587, + "143464": 5774, + "143467": 7936, + "143468": 6645, + "143475": 4973, + "143476": 6232, + "143480": 5287, + "143481": 5703, + "143482": 2446, + "143483": 7127, + "143488": 3164, + "143489": 2509, + "143495": 7775, + "143497": 5481, + "143498": 4966, + "143500": 7013, + "143501": 6549, + "143502": 2014, + "143506": 4448, + "143510": 7844, + "143511": 523, + "143513": 6054, + "143514": 7202, + "143515": 1241, + "143520": 6336, + "143522": 6900, + "143524": 5775, + "143529": 6663, + "143531": 2093, + "143532": 3887, + "143534": 3037, + "143535": 6417, + "143540": 5833, + "143548": 5445, + "143562": 3888, + "143563": 7358, + "143564": 1792, + "143578": 7138, + "143579": 5492, + "143580": 5208, + "143586": 2438, + "143592": 4000, + "143593": 2605, + "143597": 2701, + "143598": 8175, + "143604": 5288, + "143622": 7611, + "143626": 6430, + "143632": 7836, + "143635": 8652, + "143637": 7390, + "143638": 6429, + "143696": 2748, + "143703": 7085, + "143705": 7364, + "143784": 7742, + "143807": 6230, + "143810": 3916, + "143833": 1010, + "143839": 7743, + "143844": 5493, + "143851": 4947, + "143855": 2530, + "143867": 3374, + "143876": 3868, + "143878": 4511, + "143909": 3730, + "143927": 2450, + "143930": 4677, + "143939": 1791, + "143942": 2692, + "143960": 3633, + "143983": 4939, + "143986": 7213, + "144006": 5541, + "144007": 7871, + "144008": 239, + "162071": 4486, + "162072": 2022, + "162073": 2532, + "162074": 3993, + "162075": 3995, + "162077": 6355, + "162079": 3991, + "162080": 3876, + "162081": 7059, + "162082": 7565, + "162086": 7115, + "162087": 5838, + "162090": 3992, + "162091": 7057, + "162092": 5446, + "162095": 7864, + "162100": 1327, + "162102": 3977, + "162104": 8472, + "162107": 6468, + "162108": 920, + "162109": 6766, + "162111": 4833, + "162114": 3168, + "162116": 5704, + "162119": 5294, + "162120": 5543, + "162121": 7139, + "162124": 8506, + "162128": 5787, + "162130": 3167, + "162134": 8079, + "162135": 2606, + "162138": 1216, + "162139": 8727, + "162140": 7874, + "162141": 8126, + "162142": 6651, + "162144": 7393, + "162146": 3440, + "162147": 8706, + "162148": 4948, + "162151": 8640, + "162152": 5579, + "162154": 8568, + "162156": 8084, + "162157": 7582, + "162158": 7205, + "162163": 7837, + "162172": 8720, + "162174": 3042, + "162175": 3393, + "162182": 4942, + "162188": 3651, + "162194": 2281, + "162196": 3505, + "162199": 3443, + "162202": 6856, + "162203": 6821, + "162204": 8191, + "162206": 6847, + "162209": 8531, + "162213": 6117, + "162214": 8064, + "162218": 4854, + "162222": 5522, + "162224": 4326, + "162225": 7679, + "162226": 7580, + "162232": 4713, + "162236": 5609, + "162237": 5692, + "162239": 7873, + "162247": 4704, + "162248": 3442, + "162249": 8174, + "162252": 4400, + "162255": 4274, + "162258": 2282, + "162267": 6096, + "162272": 7641, + "162280": 8132, + "162284": 8013, + "162288": 6483, + "162290": 3230, + "162293": 7790, + "162294": 7589, + "162297": 3439, + "162299": 6551, + "162301": 5282, + "162303": 8634, + "162304": 5281, + "162308": 6327, + "162314": 7394, + "162318": 7862, + "162319": 7870, + "162320": 5834, + "162323": 6098, + "162325": 7845, + "162328": 4703, + "162329": 4959, + "162332": 4941, + "162336": 7563, + "162343": 8657, + "162346": 3365, + "162347": 8267, + "162349": 5542, + "162350": 3765, + "162351": 8143, + "162357": 4399, + "162364": 5680, + "162365": 8189, + "162366": 4001, + "162368": 5350, + "162369": 6454, + "162371": 7055, + "162376": 6238, + "162380": 4537, + "162382": 3994, + "162385": 7937, + "162386": 8690, + "162387": 5588, + "162388": 5931, + "162392": 2843, + "162395": 8572, + "162396": 5776, + "162398": 3043, + "162402": 6364, + "162403": 2266, + "162404": 2510, + "162411": 4613, + "162416": 8150, + "162418": 5494, + "162425": 6553, + "162428": 5391, + "162431": 3797, + "162435": 6634, + "162439": 6431, + "162441": 8017, + "162446": 4209, + "162447": 4538, + "162448": 4622, + "162449": 5484, + "162451": 7010, + "162454": 8328, + "162458": 6174, + "162465": 8570, + "162466": 580, + "162477": 581, + "162478": 7309, + "162479": 8033, + "162480": 4458, + "162482": 4552, + "162487": 7613, + "162488": 8327, + "162491": 4974, + "162493": 8263, + "162494": 5521, + "162499": 8512, + "162510": 2265, + "162515": 579, + "162519": 8405, + "162520": 6356, + "162521": 4528, + "162525": 3836, + "162529": 3441, + "162532": 4236, + "162534": 4031, + "162535": 8451, + "162540": 7311, + "162546": 5880, + "162549": 3986, + "162552": 7774, + "162555": 4529, + "162563": 6824, + "162569": 7204, + "162571": 5616, + "162575": 8246, + "162578": 8569, + "162581": 4712, + "162583": 5120, + "162587": 6472, + "162607": 7640, + "162609": 8127, + "162611": 2852, + "162614": 5390, + "162620": 5806, + "162621": 2899, + "162623": 7016, + "162630": 3890, + "162633": 6912, + "162636": 5777, + "162645": 3982, + "162646": 6664, + "162648": 7329, + "162653": 5482, + "162655": 6056, + "162661": 8571, + "162667": 8630, + "162668": 7547, + "162672": 8404, + "162674": 6515, + "162677": 5918, + "162678": 3889, + "162680": 7310, + "162693": 5425, + "162694": 6476, + "162695": 5582, + "162708": 5512, + "162711": 6646, + "162717": 1258, + "162720": 8501, + "162724": 4328, + "162725": 2888, + "162726": 5785, + "162727": 8505, + "162730": 8165, + "162748": 8510, + "162753": 6973, + "162759": 4967, + "162766": 2700, + "162768": 2788, + "162771": 8555, + "162772": 7365, + "162773": 7581, + "162822": 7745, + "162825": 4199, + "162831": 4665, + "162834": 2455, + "162835": 8409, + "162839": 3959, + "162841": 6231, + "162846": 7760, + "162856": 7110, + "162858": 8635, + "162864": 4666, + "162865": 6233, + "162866": 8235, + "162869": 8632, + "162870": 8636, + "162871": 7612, + "162873": 6888, + "162874": 3799, + "162881": 8090, + "162882": 2954, + "162891": 905, + "162903": 3166, + "162911": 3165, + "162918": 7094, + "162920": 5544, + "162961": 4289, + "162968": 8578, + "162977": 2841, + "162979": 7361, + "174093": 4534, + "174095": 5279, + "174096": 8262, + "174097": 4940, + "174098": 4457, + "174101": 5774, + "174103": 4663, + "174105": 4710, + "174107": 7214, + "174108": 7053, + "174109": 8705, + "174110": 4852, + "174111": 6055, + "174113": 6173, + "174114": 5520, + "174117": 8325, + "174118": 6097, + "174121": 4484, + "174122": 4702, + "174123": 2870, + "174124": 5608, + "174125": 6878, + "174126": 5424, + "174130": 7773, + "174131": 6326, + "174132": 9392, + "174134": 5915, + "174135": 6054, + "174140": 4832, + "174141": 3435, + "174142": 6633, + "174145": 8063, + "174146": 2873, + "174148": 7392, + "174150": 3730, + "174152": 4939, + "174154": 8078, + "174155": 7114, + "174157": 2953, + "174158": 8131, + "174159": 9278, + "174160": 8082, + "174161": 7006, + "174162": 9311, + "174163": 4526, + "174165": 8719, + "174168": 7009, + "174169": 5540, + "174171": 3437, + "174172": 6972, + "174173": 6663, + "174176": 2692, + "174177": 7308, + "174180": 8172, + "174181": 4485, + "174183": 5916, + "174184": 3835, + "174185": 7789, + "174186": 7138, + "174187": 5280, + "174188": 4711, + "174191": 1126, + "174192": 5208, + "174194": 5817, + "174195": 7107, + "174196": 4527, + "174198": 4397, + "174199": 3041, + "174201": 7328, + "174202": 7546, + "174204": 6453, + "174205": 7980, + "174206": 4535, + "174207": 5688, + "174208": 6230, + "174209": 3795, + "174210": 4664, + "174211": 2840, + "174212": 6854, + "174213": 3373, + "174218": 3040, + "174221": 3438, + "174222": 1794, + "174229": 5519, + "174230": 8726, + "174234": 5879, + "174241": 4207, + "174244": 2509, + "174245": 3981, + "174246": 7638, + "174249": 8641, + "174254": 5832, + "174257": 4973, + "174258": 9301, + "174260": 7357, + "174266": 2531, + "174270": 3887, + "174273": 6363, + "174274": 4213, + "174276": 9264, + "174281": 3834, + "174282": 8629, + "174285": 5541, + "174288": 3613, + "174291": 3436, + "174295": 6095, + "174297": 523, + "174298": 7742, + "174301": 7108, + "174302": 1069, + "174303": 4853, + "174305": 6466, + "174306": 7054, + "174307": 5775, + "174308": 2409, + "174310": 8530, + "174311": 6820, + "174314": 5293, + "174316": 7863, + "174318": 3460, + "174319": 6310, + "174321": 4088, + "174322": 8245, + "174323": 5687, + "174327": 8566, + "174333": 2021, + "174336": 3110, + "174337": 3374, + "174339": 5561, + "174345": 7743, + "174346": 5930, + "174347": 7588, + "174352": 8015, + "174353": 5492, + "174359": 8088, + "174361": 8125, + "174362": 6645, + "174364": 6467, + "174367": 7512, + "174370": 5445, + "174372": 7215, + "174373": 9372, + "174375": 6237, + "174376": 3164, + "174377": 7861, + "174378": 8577, + "174379": 7015, + "174380": 7557, + "174384": 8326, + "174385": 6417, + "174388": 6765, + "174389": 2262, + "174390": 5511, + "174395": 6550, + "174397": 6430, + "174401": 7390, + "174402": 2280, + "174403": 5679, + "174408": 2478, + "174412": 6429, + "174413": 7936, + "174414": 1034, + "174415": 8656, + "174417": 3229, + "174422": 5162, + "174423": 3392, + "174425": 2605, + "174440": 2263, + "174441": 1656, + "174442": 7239, + "174446": 1479, + "174448": 3984, + "174449": 4958, + "174453": 6081, + "174457": 2883, + "174460": 5481, + "174472": 5577, + "174479": 5703, + "174482": 239, + "174488": 1801, + "174492": 3361, + "174505": 950, + "174508": 1802, + "174509": 1241, + "174514": 7844, + "174516": 6846, + "174519": 9335, + "174521": 3990, + "174525": 727, + "174529": 4272, + "174532": 8032, + "174538": 6549, + "174547": 7062, + "174548": 7678, + "174549": 8664, + "174551": 7869, + "174568": 6650, + "174569": 7127, + "174572": 7809, + "174575": 7013, + "174576": 6354, + "174577": 3976, + "174594": 960, + "174602": 7212, + "174603": 2279, + "174607": 3362, + "174609": 5287, + "174614": 3163, + "174619": 1790, + "174620": 3699, + "174629": 673, + "174656": 6457, + "174658": 3504, + "174661": 5288, + "174674": 4966, + "174675": 7562, + "174677": 8471, + "174680": 2783, + "174687": 4947, + "174708": 7871, + "174709": 7872, + "174715": 1928, + "174723": 3888, + "174724": 2446, + "174727": 7639, + "174728": 8504, + "174734": 2438, + "174747": 1792, + "174768": 8518, + "174782": 2264, + "174810": 7218, + "174811": 3111, + "174820": 7202, + "174834": 6901, + "174840": 3633, + "174858": 7203, + "174874": 5576, + "174897": 9281, + "174904": 1480, + "174933": 964, + "174957": 3985, + "174966": 6403, + "174967": 1010, + "174972": 4028, + "174983": 4029, + "175019": 4273, + "175023": 8130, + "175028": 1147, + "175041": 7318, + "175074": 8479, + "175111": 7561, + "175128": 1035, + "175135": 1036, + "175151": 8403, + "175159": 1517, + "175262": 3265, + "207560": 8899, + "207563": 6766, + "207564": 1809, + "207569": 9140, + "207570": 7309, + "207572": 4974, + "207574": 3995, + "207575": 4665, + "207578": 8064, + "207581": 9082, + "207582": 3993, + "207584": 8640, + "207586": 2606, + "207588": 8727, + "207590": 9042, + "207593": 2510, + "207594": 6551, + "207595": 2281, + "207597": 5838, + "207598": 8132, + "207599": 8506, + "207601": 8510, + "207605": 4399, + "207606": 9115, + "207609": 8706, + "207611": 6821, + "207612": 8720, + "207614": 3797, + "207619": 920, + "207621": 6468, + "207622": 6847, + "207623": 6174, + "207626": 4713, + "207632": 5588, + "207633": 6364, + "207634": 9393, + "207635": 4486, + "207636": 8941, + "207637": 4001, + "207639": 7115, + "207640": 6634, + "207641": 4209, + "207642": 4948, + "207643": 1334, + "207644": 8246, + "207647": 6238, + "207648": 7937, + "207651": 4458, + "207652": 8950, + "207653": 9312, + "207654": 2954, + "207660": 2955, + "207662": 8143, + "207664": 8951, + "207670": 7679, + "207671": 579, + "207672": 8126, + "207673": 5281, + "207674": 9313, + "207676": 7862, + "207678": 3889, + "207680": 7057, + "207681": 3442, + "207683": 4704, + "207684": 7845, + "207686": 9141, + "207687": 4703, + "207690": 8942, + "207694": 3166, + "207699": 3982, + "207702": 8944, + "207703": 3167, + "207707": 3439, + "207810": 6651, + "207812": 8394, + "207817": 6083, + "207819": 7139, + "207820": 3876, + "207822": 8033, + "207831": 6483, + "207832": 3043, + "207834": 4529, + "207838": 2841, + "207841": 9056, + "207842": 8873, + "207843": 5787, + "207848": 4528, + "207851": 2532, + "207852": 2461, + "207856": 3991, + "207857": 4941, + "207860": 7393, + "207861": 7094, + "207863": 8017, + "207864": 8568, + "207874": 6454, + "207876": 4959, + "207879": 3042, + "207881": 4236, + "207882": 2479, + "207883": 2266, + "207885": 9265, + "207887": 6056, + "207891": 1821, + "207893": 9055, + "207894": 5484, + "207895": 7055, + "207897": 2022, + "207906": 9130, + "207907": 8570, + "207916": 8657, + "207922": 3992, + "207924": 2265, + "207926": 8263, + "207930": 8782, + "207932": 3440, + "207933": 4854, + "207935": 4537, + "207939": 7640, + "207940": 6553, + "207942": 5282, + "207948": 7774, + "207949": 3987, + "207950": 6646, + "207952": 9336, + "207953": 9302, + "207956": 1832, + "207957": 5482, + "207958": 2843, + "207961": 8572, + "207966": 3651, + "207967": 9208, + "207971": 4274, + "207972": 8569, + "207981": 1216, + "207986": 2563, + "207988": 2282, + "207989": 5931, + "207995": 5680, + "207996": 8573, + "207997": 6431, + "207999": 2455, + "208002": 8189, + "208003": 1929, + "208011": 5512, + "208017": 3443, + "208019": 8789, + "208241": 8267, + "208246": 1485, + "208247": 9283, + "208271": 4833, + "208275": 4666, + "208276": 6472, + "208280": 4282, + "208281": 7016, + "208282": 3799, + "208287": 5880, + "208290": 7870, + "208291": 5119, + "208293": 4326, + "208294": 6355, + "208297": 6117, + "208299": 7205, + "208301": 8085, + "208302": 5446, + "208303": 7010, + "208310": 8509, + "208315": 6973, + "208320": 6476, + "208324": 3890, + "208325": 5806, + "208348": 8472, + "208356": 5290, + "208365": 8034, + "208368": 2888, + "208376": 6327, + "208380": 8165, + "208386": 1660, + "208388": 4030, + "208394": 9337, + "208409": 7874, + "208410": 5777, + "208412": 2852, + "208425": 3230, + "208430": 4400, + "208431": 7565, + "208436": 7059, + "208440": 4712, + "208441": 5776, + "208442": 5834, + "208444": 3393, + "208451": 7582, + "208458": 7563, + "208459": 7109, + "208460": 4967, + "208462": 6233, + "208464": 7110, + "208468": 5839, + "208508": 7938, + "208509": 6096, + "208514": 8630, + "208518": 7790, + "208523": 9279, + "208528": 3505, + "208537": 3994, + "208553": 2460, + "208569": 5917, + "208574": 9086, + "208578": 8987, + "208582": 8531, + "208588": 9394, + "208590": 9215, + "208608": 8091, + "208611": 8052, + "208612": 3165, + "208614": 9203, + "208623": 7983, + "208629": 9006, + "208630": 7873, + "208634": 8690, + "208642": 6664, + "208653": 5294, + "208659": 9339, + "208662": 4942, + "208683": 7864, + "208684": 8327, + "208685": 2842, + "208690": 4831, + "208694": 7981, + "208697": 7329, + "208702": 7641, + "208704": 8090, + "208705": 1805, + "208714": 7860, + "208723": 9284, + "208725": 6856, + "208726": 4328, + "208735": 5543, + "208742": 1806, + "208744": 9116, + "208758": 6891, + "208761": 1149, + "208764": 1258, + "208780": 7204, + "208788": 5704, + "208798": 7587, + "208805": 4552, + "208806": 5690, + "208811": 9128, + "208815": 8404, + "208823": 5582, + "208828": 7391, + "208830": 5521, + "208835": 8634, "214728": 7871, - "215173": 7773, - "215440": 1799, - "214906": 9214, - "215038": 2873, - "215135": 2883, - "215438": 3041, - "215052": 8719, - "214959": 4939, - "215169": 5287, - "215557": 5576, - "214889": 1147, - "215529": 4484, - "215421": 7639, - "214917": 2840, - "215306": 6650, - "215261": 4457, - "215137": 3373, - "215409": 7127, - "215012": 3504, - "215294": 4088, - "215231": 3229, - "215151": 5837, - "215124": 523, - "215283": 6972, - "215274": 3985, - "215032": 8705, - "215080": 6097, + "214730": 9335, + "214732": 8566, + "214734": 6549, + "214735": 1069, + "214737": 2509, "214740": 7588, - "215088": 2279, - "214946": 5832, - "214793": 8883, - "214950": 4702, - "214919": 2953, - "215351": 7863, - "214940": 9392, - "214749": 7392, - "215402": 7561, - "215476": 8078, - "215289": 3887, - "215077": 3835, - "215036": 8245, - "215125": 950, - "214758": 7203, - "214837": 9972, - "214789": 2280, "214742": 8781, - "215528": 3111, - "215215": 4710, - "215262": 3976, - "215394": 5293, + "214744": 1654, + "214745": 9053, + "214748": 7980, + "214749": 7392, + "214750": 7512, + "214755": 6846, + "214757": 7546, + "214758": 7203, + "214759": 5930, + "214760": 9372, + "214763": 9139, + "214766": 8210, + "214768": 9080, + "214773": 9980, + "214774": 8015, + "214775": 6453, + "214782": 4466, + "214789": 2280, + "214793": 8883, + "214795": 6663, + "214809": 7742, + "214812": 9264, + "214814": 2262, + "214820": 4028, + "214822": 955, + "214831": 7308, + "214832": 2478, + "214834": 7357, + "214836": 9278, + "214837": 9972, + "214843": 8577, + "214846": 1802, + "214847": 8641, + "214855": 8530, + "214859": 7114, + "214860": 4852, + "214861": 4940, + "214862": 3990, + "214867": 7678, + "214869": 525, + "214872": 3834, + "214876": 7215, + "214882": 7009, + "214885": 7006, + "214886": 3164, + "214889": 1147, + "214894": 10004, + "214895": 8504, + "214896": 4207, + "214900": 9281, + "214902": 7053, + "214903": 6326, + "214904": 5520, + "214906": 9214, + "214907": 7239, + "214908": 4947, + "214912": 6550, + "214915": 7015, + "214917": 2840, + "214919": 2953, + "214922": 3460, + "214923": 7202, + "214924": 6765, + "214926": 9126, + "214927": 2021, + "214930": 2605, + "214932": 5519, + "214934": 1126, + "214935": 2692, + "214940": 9392, + "214941": 4273, + "214943": 7062, + "214946": 5832, + "214950": 4702, + "214951": 7861, + "214953": 7562, + "214955": 5608, + "214956": 3361, + "214958": 5116, + "214959": 4939, + "214964": 4397, + "214969": 3888, + "214970": 7318, + "214972": 2783, + "214973": 4485, + "214978": 4511, + "214981": 4853, + "214983": 2531, + "214984": 10007, + "214993": 8063, + "214997": 3795, + "214999": 7557, + "215001": 4966, + "215002": 8726, + "215004": 5280, + "215007": 3438, + "215009": 2701, + "215012": 3504, + "215015": 8948, + "215019": 1479, + "215020": 7936, + "215022": 8787, + "215023": 3633, + "215024": 239, + "215026": 9373, + "215027": 6633, + "215029": 1034, + "215032": 8705, + "215033": 2057, + "215036": 8245, + "215038": 2873, + "215039": 1928, + "215041": 6081, + "215046": 8403, + "215052": 8719, + "215055": 8939, + "215057": 6055, + "215060": 9999, + "215076": 7138, + "215077": 3835, + "215078": 3436, + "215080": 6097, + "215084": 3163, + "215085": 3730, + "215086": 8325, + "215088": 2279, + "215092": 8131, + "215093": 7872, + "215107": 2263, + "215108": 4527, "215109": 6820, - "215405": 5208 + "215111": 4973, + "215116": 3981, + "215120": 8172, + "215122": 5915, + "215124": 523, + "215125": 950, + "215127": 6429, + "215132": 6645, + "215135": 2883, + "215137": 3373, + "215138": 8949, + "215139": 6114, + "215141": 7107, + "215144": 3435, + "215145": 2264, + "215148": 6054, + "215151": 5837, + "215154": 6095, + "215157": 7221, + "215162": 9301, + "215164": 9114, + "215165": 3392, + "215166": 9054, + "215167": 3110, + "215169": 5287, + "215173": 7773, + "215174": 5424, + "215176": 8471, + "215177": 8175, + "215178": 6173, + "215179": 2014, + "215186": 10072, + "215192": 9311, + "215199": 7213, + "215200": 3437, + "215208": 3362, + "215209": 4663, + "215212": 8125, + "215213": 8082, + "215215": 4710, + "215219": 6457, + "215223": 5481, + "215226": 8943, + "215227": 3984, + "215231": 3229, + "215232": 9987, + "215236": 9129, + "215237": 7013, + "215238": 4000, + "215255": 7108, + "215258": 4958, + "215261": 4457, + "215262": 3976, + "215264": 5687, + "215269": 2438, + "215270": 3699, + "215273": 6467, + "215274": 3985, + "215283": 6972, + "215289": 3887, + "215294": 4088, + "215300": 10009, + "215305": 8088, + "215306": 6650, + "215317": 6466, + "215331": 2446, + "215344": 1790, + "215351": 7863, + "215355": 1796, + "215359": 5445, + "215361": 727, + "215378": 1477, + "215383": 4526, + "215394": 5293, + "215395": 4534, + "215398": 7328, + "215402": 7561, + "215405": 5208, + "215408": 2870, + "215409": 7127, + "215417": 7054, + "215419": 8083, + "215421": 7639, + "215433": 6854, + "215437": 6363, + "215438": 3041, + "215440": 1799, + "215443": 4711, + "215447": 9113, + "215452": 3040, + "215471": 6354, + "215472": 5774, + "215474": 7218, + "215476": 8078, + "215480": 1794, + "215484": 7638, + "215486": 8940, + "215489": 8262, + "215494": 65, + "215505": 4272, + "215514": 5833, + "215518": 5748, + "215521": 4664, + "215528": 3111, + "215529": 4484, + "215538": 6237, + "215540": 9282, + "215544": 6878, + "215547": 7743, + "215550": 8922, + "215557": 5576, + "215562": 9197, + "215593": 8656, + "215600": 8188, + "215609": 5577, + "215634": 673, + "215636": 1035 } diff --git a/src/lib/server/animebracket/resolvers.js b/src/lib/server/animebracket/resolvers.js index 96c0ba2..e7b7d14 100644 --- a/src/lib/server/animebracket/resolvers.js +++ b/src/lib/server/animebracket/resolvers.js @@ -1,20 +1,38 @@ +import brackets from "./brackets.json"; import mappings from "./mappings.json"; import { apiResolver } from "lib/common/animethemes/api"; +import devLog from "utils/devLog"; const resolvers = { Query: { bracket: async (_, { slug }) => { - const bracket = await fetchJson(`https://animebracket.com/api/bracket/${slug}`); - const rounds = await fetchJson(`https://animebracket.com/api/results/${slug}`); - const currentRound = await fetchJson(`https://animebracket.com/api/rounds/${slug}`); + const config = brackets[slug]; + + if (!config) { + return null; + } + + devLog.info(`Fetching bracket: ${config.slug}`); + const bracket = await fetchJson(`https://animebracket.com/api/bracket/${config.slug}`); + const rounds = await fetchJson(`https://animebracket.com/api/results/${config.slug}`); + // Some brackets are broken and still in a running state. We can check if the bracket has a winner already and + // not fetch the current round in that case. + const currentRound = bracket.winnerCharacterId ? null : await fetchJson(`https://animebracket.com/api/rounds/${config.slug}`); + devLog.info(`Fetched bracket: ${config.slug} (${rounds.length} rounds)`); return { slug, - name: bracket.name, + name: config.name, currentRound, currentGroup: currentRound, rounds }; + }, + bracketAll: async () => { + return Object.entries(brackets).map(([slug, bracket]) => ({ + slug, + name: bracket.name, + })); } }, Bracket: { diff --git a/src/lib/server/animebracket/type-defs.js b/src/lib/server/animebracket/type-defs.js index 5a90496..70318ca 100644 --- a/src/lib/server/animebracket/type-defs.js +++ b/src/lib/server/animebracket/type-defs.js @@ -3,6 +3,7 @@ const typeDefs = ` type Query { bracket(slug: String): Bracket + bracketAll: [Bracket] } type Bracket { diff --git a/src/pages/_app.js b/src/pages/_app.js index 29a590e..3506a8c 100644 --- a/src/pages/_app.js +++ b/src/pages/_app.js @@ -15,8 +15,6 @@ import Head from "next/head"; import withBasePath from "utils/withBasePath"; import { SEO } from "components/seo"; import { config } from "@fortawesome/fontawesome-svg-core"; -import { WatchHistoryProvider } from "context/watchHistoryContext"; -import { LocalPlaylistProvider } from "context/localPlaylistContext"; import { ToastProvider } from "context/toastContext"; import { AnnouncementToast, ToastHub } from "components/toast"; import { Text } from "components/text"; @@ -24,6 +22,9 @@ import { useRouter } from "next/router"; import useSetting from "hooks/useSetting"; import { DeveloperMode, RevalidationToken } from "utils/settings"; import { ErrorBoundary } from "components/utils"; +import { STAGING } from "utils/config"; +import { Card } from "components/card"; +import { ExternalLink } from "components/external-link"; import "@fortawesome/fontawesome-svg-core/styles.css"; import "styles/prism.scss"; @@ -46,7 +47,7 @@ const StyledContainer = styled(Container)` export default function MyApp({ Component, pageProps }) { const router = useRouter(); - const [colorTheme, toggleColorTheme] = useColorTheme(); + const [colorTheme, setColorTheme] = useColorTheme(); const [developerMode] = useSetting(DeveloperMode); const { lastBuildAt, apiRequests, isVideoPage = false, ...videoPageProps } = pageProps; @@ -74,14 +75,12 @@ export default function MyApp({ Component, pageProps }) { return ( setLastVideoPageProps(null) } } ], [ QueryClientProvider, { client: queryClient } ], - [ WatchHistoryProvider ], - [ LocalPlaylistProvider ], [ ToastProvider, { initialToasts: [ { id: "announcement", content: @@ -113,6 +112,17 @@ export default function MyApp({ Component, pageProps }) { {pageProps.isSearch && ( )} + {STAGING ? ( + + + WARNING: This version of the website is for testing purposes only. + Some pages or functions might not work. + + + CLICK HERE TO GO BACK TO THE NORMAL SITE + + + ) : null} {developerMode === DeveloperMode.ENABLED && lastBuildAt && ( diff --git a/src/pages/_document.js b/src/pages/_document.js index 5921263..dce0d2c 100644 --- a/src/pages/_document.js +++ b/src/pages/_document.js @@ -5,7 +5,7 @@ const ThemeInjection = () => { // language=JavaScript const injectTheme = ` (function() { - let theme = window.localStorage.getItem("theme"); + let theme = JSON.parse(window.localStorage.getItem("theme")); if (!theme && window.matchMedia("(prefers-color-scheme: light)").matches) { theme = "light"; } @@ -34,7 +34,7 @@ export default class MyDocument extends Document { rel="stylesheet" /> - +
diff --git a/src/pages/anime/[animeSlug]/[videoSlug]/index.js b/src/pages/anime/[animeSlug]/[videoSlug]/index.js index f1e02b4..a861a82 100644 --- a/src/pages/anime/[animeSlug]/[videoSlug]/index.js +++ b/src/pages/anime/[animeSlug]/[videoSlug]/index.js @@ -8,11 +8,10 @@ import { AnimeSummaryCard, ArtistSummaryCard, SummaryCard, ThemeSummaryCard } fr import useImage from "hooks/useImage"; import { fetchData } from "lib/server"; import { SEO } from "components/seo"; -import { videoBaseUrl } from "lib/client/api"; import { faChevronDown, faMinus, faPlus, faShare } from "@fortawesome/pro-solid-svg-icons"; import { Icon } from "components/icon"; -import { useWatchHistory } from "context/watchHistoryContext"; -import { useLocalPlaylist } from "context/localPlaylistContext"; +import useWatchHistory from "hooks/useWatchHistory"; +import useLocalPlaylist from "hooks/useLocalPlaylist"; import styled from "styled-components"; import theme from "theme"; import createVideoSlug from "utils/createVideoSlug"; @@ -23,6 +22,7 @@ import { Menu } from "components/menu"; import { useToasts } from "context/toastContext"; import { Toast } from "components/toast"; import { ThemeEntryTags, VideoTags } from "components/tag"; +import { VIDEO_URL } from "utils/config"; const StyledVideoInfo = styled.div` display: grid; @@ -166,7 +166,7 @@ export default function VideoPage({ anime, theme, entry, video }) { .then(() => dispatchToast("clipboard", Copied to clipboard!)); }; - const videoUrl = `${videoBaseUrl}/${video.basename}`; + const videoUrl = `${VIDEO_URL}/${video.basename}`; return ( <> @@ -420,7 +420,7 @@ export async function getStaticProps({ params: { animeSlug, videoSlug } }) { } export async function getStaticPaths() { - return fetchStaticPaths(async (isStaging) => { + return fetchStaticPaths(async () => { const { data } = await fetchData(gql` query { animeAll { @@ -441,14 +441,7 @@ export async function getStaticPaths() { } `); - let anime = data.animeAll; - - if (isStaging) { - // In staging, we only want to pre-render the video pages for the neweset 100 anime. - anime = anime.sort((a, b) => b.id - a.id).slice(0, 100); - } - - return anime.flatMap( + return data.animeAll.flatMap( (anime) => anime.themes.flatMap( (theme) => theme.entries.flatMap( (entry) => entry.videos.flatMap( diff --git a/src/pages/anime/[animeSlug]/index.js b/src/pages/anime/[animeSlug]/index.js index b83e95f..e2617d6 100644 --- a/src/pages/anime/[animeSlug]/index.js +++ b/src/pages/anime/[animeSlug]/index.js @@ -224,7 +224,7 @@ export async function getStaticProps({ params: { animeSlug } }) { } export async function getStaticPaths() { - return fetchStaticPaths(async (isStaging) => { + return fetchStaticPaths(async () => { const { data } = await fetchData(gql` query { animeAll { @@ -234,14 +234,7 @@ export async function getStaticPaths() { } `); - let anime = data.animeAll; - - if (isStaging) { - // In staging, we only want to pre-render the newest 100 anime pages. - anime = anime.sort((a, b) => b.id - a.id).slice(0, 100); - } - - return anime.map((anime) => ({ + return data.animeAll.map((anime) => ({ params: { animeSlug: anime.slug } diff --git a/src/pages/api/revalidate.js b/src/pages/api/revalidate.js index 688ef05..22b585e 100644 --- a/src/pages/api/revalidate.js +++ b/src/pages/api/revalidate.js @@ -1,11 +1,12 @@ import { fetchData } from "lib/server"; import gql from "graphql-tag"; import createVideoSlug from "utils/createVideoSlug"; +import { BASE_PATH, REVALIDATE_TOKEN } from "utils/config"; export default async function handler(req, res) { const { secret, id, resource, mode = "page" } = req.query; - if (secret !== process.env.REVALIDATE_TOKEN) { + if (secret !== REVALIDATE_TOKEN) { return res.status(401).json({ message: "Invalid token." }); } @@ -28,7 +29,7 @@ export default async function handler(req, res) { } for (const path of paths) { - await res.unstable_revalidate(`${process.env.NEXT_PUBLIC_BASE_PATH || ""}${path}`); + await res.unstable_revalidate(`${BASE_PATH}${path}`); } return res.json({ revalidated: true, affectedPaths: paths }); diff --git a/src/pages/design.js b/src/pages/design.js index cbfa16f..99938d7 100644 --- a/src/pages/design.js +++ b/src/pages/design.js @@ -18,7 +18,7 @@ import { import { Icon } from "components/icon"; import styled from "styled-components"; import theme from "theme"; -import { Collapse, HeightTransition, Highlight } from "components/utils"; +import { Collapse, HeightTransition } from "components/utils"; import ColorThemeContext from "context/colorThemeContext"; import { ExternalLink } from "components/external-link"; import { Switcher } from "components/switcher"; @@ -35,6 +35,7 @@ import { useToasts } from "context/toastContext"; import { Input } from "components/input"; import gql from "graphql-tag"; import getSharedPageProps from "utils/getSharedPageProps"; +import { Highlight } from "components/utils/Highlight"; const ColorGrid = styled.div` display: grid; diff --git a/src/pages/event/[bracketSlug]/index.js b/src/pages/event/[bracketSlug]/index.js index a728ff5..654c039 100644 --- a/src/pages/event/[bracketSlug]/index.js +++ b/src/pages/event/[bracketSlug]/index.js @@ -1,36 +1,24 @@ import styled from "styled-components"; -import Link from "next/link"; import { SEO } from "components/seo"; import { Text } from "components/text"; -import { Column } from "components/box"; -import { SummaryCard } from "components/card"; -import { faAward, faSeedling, faStopwatch, faUsers } from "@fortawesome/pro-solid-svg-icons"; -import { Icon } from "components/icon"; +import { Column, Solid } from "components/box"; +import { faStopwatch } from "@fortawesome/pro-solid-svg-icons"; import theme from "theme"; -import useImage from "hooks/useImage"; -import createVideoSlug from "utils/createVideoSlug"; -import { SongTitleWithArtists } from "components/utils"; import { useState } from "react"; import { Switcher } from "components/switcher"; import { fetchData } from "lib/server"; import getSharedPageProps from "utils/getSharedPageProps"; +import fetchStaticPaths from "utils/fetchStaticPaths"; +import gql from "graphql-tag"; +import { CornerIcon } from "components/icon/CornerIcon"; +import { BracketThemeSummaryCard } from "components/bracket/BracketThemeSummaryCard"; +import { BracketChart } from "components/bracket/BracketChart"; -const CornerIcon = styled(Icon).attrs({ - size: "2x" -})` - position: absolute; - right: 0; - top: 0; - color: ${theme.colors["text-primary"]}; - transform: translate(50%, -33%) rotate(10deg); -`; - -const CurrentRound = styled.div` +const CurrentRound = styled(Solid)` position: relative; margin: 0 -2rem; padding: 2rem; border-radius: 0.5rem; - background-color: ${theme.colors["solid"]}; `; const StyledHeader = styled.div` @@ -56,6 +44,7 @@ export default function BracketPage({ bracket }) { <> {bracket.name} + {!!bracket.currentRound && ( )} @@ -147,8 +136,8 @@ function ContestantCard({ contestant, opponent, contestantVotes, opponentVotes } const isWinner = isVoted && (contestantVotes !== opponentVotes ? contestantVotes > opponentVotes : contestant.seed < opponent.seed); return ( - - {theme.slug} - - {theme.anime.name} - - - ); - - return ( - } - description={description} - image={smallCover} - to={to} - style={{ "--opacity": !isVoted || isWinner ? 1 : 0.5 }} - {...props} - > - - - - {seed} - - {isVoted && ( - - - {votes} - - )} - - {isWinner && ( - - )} - - ); -} - export async function getStaticProps({ params: { bracketSlug } }) { const { data, apiRequests } = await fetchData(` #graphql @@ -235,6 +156,7 @@ export async function getStaticProps({ params: { bracketSlug } }) { name source theme { + id slug anime { slug @@ -312,14 +234,19 @@ export async function getStaticProps({ params: { bracketSlug } }) { } export async function getStaticPaths() { - return { - paths: [ - { - params: { - bracketSlug: "best-anime-opening-ix-salty-arrow" + return fetchStaticPaths(async () => { + const { data } = await fetchData(gql` + query { + bracketAll { + slug } } - ], - fallback: false - }; + `); + + return data.bracketAll.map((bracket) => ({ + params: { + bracketSlug: bracket.slug + } + })); + }, true); } diff --git a/src/pages/event/index.js b/src/pages/event/index.js index 66ea44b..394b0ca 100644 --- a/src/pages/event/index.js +++ b/src/pages/event/index.js @@ -12,40 +12,42 @@ import getSharedPageProps from "utils/getSharedPageProps"; const BigButton = styled(Button)` overflow: hidden; - border-radius: 8px; + border-radius: ${theme.scalars.borderRadiusCard}; width: 100%; - height: 64px; + height: 48px; justify-content: flex-end; text-align: end; - gap: 8px; + gap: 16px; `; -const BigIcon = styled(Icon)` - margin-right: auto; - - font-size: 2em; +const BigIcon = styled(Icon)` + font-size: 1.5em; color: ${theme.colors["text-disabled"]}; `; -const StyledEventGrid = styled.div` - display: grid; - grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); +const StyledEventList = styled.div` + display: flex; + flex-direction: column; grid-gap: 16px; `; +const StyledEventName = styled(Text)` + margin-right: auto; +`; + export default function EventPage({ awards, brackets }) { return ( <> Events - + Awards {awards.map(({ name, path }) => ( - {name} + {name} @@ -57,13 +59,13 @@ export default function EventPage({ awards, brackets }) { - {name} + {name} ))} - + ); } @@ -75,32 +77,26 @@ export async function getStaticProps() { path: "/event/anime-awards" } ]; - let totalApiRequests = 0; - const brackets = await Promise.all([ - "best-anime-opening-ix-salty-arrow" - ].map(async (bracket) => { - const { data, apiRequests } = await fetchData(` - #graphql + const { data, apiRequests } = await fetchData(` + #graphql - query($bracketSlug: String!) { - bracket(slug: $bracketSlug) { - name - } + query { + bracketAll { + slug + name } - `, { bracketSlug: bracket }); + } + `); - totalApiRequests += apiRequests; - - return { - name: data.bracket.name, - path: `/event/${bracket}` - }; + const brackets = data.bracketAll.map((bracket) => ({ + name: bracket.name, + path: `/event/${bracket.slug}` })); return { props: { - ...getSharedPageProps(totalApiRequests), + ...getSharedPageProps(apiRequests), awards, brackets } diff --git a/src/pages/index.js b/src/pages/index.js index effa879..c42c03d 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -17,6 +17,8 @@ import { FeaturedTheme } from "components/featured-theme"; import gql from "graphql-tag"; import { fetchDataClient } from "lib/client"; import getSharedPageProps from "utils/getSharedPageProps"; +import { range } from "lodash-es"; +import { Skeleton } from "components/skeleton"; const BigButton = styled(Button)` justify-content: flex-end; @@ -82,7 +84,7 @@ const About = styled(Column)` `; export default function HomePage({ featuredTheme }) { - const [ recentlyAdded, setRecentlyAdded ] = useState([]); + const [ recentlyAdded, setRecentlyAdded ] = useState(null); const { currentYear, currentSeason } = useCurrentSeason(); useEffect(() => { @@ -116,8 +118,12 @@ export default function HomePage({ featuredTheme }) { - {recentlyAdded.map((theme, index) => ( - + {range(10).map((index) => ( + + {(recentlyAdded !== null && recentlyAdded[index]) ? ( + + ) : null} + ))} diff --git a/src/pages/profile/gallery/index.js b/src/pages/profile/gallery/index.js index dc6b933..7fe862b 100644 --- a/src/pages/profile/gallery/index.js +++ b/src/pages/profile/gallery/index.js @@ -2,7 +2,7 @@ import { Text } from "components/text"; import styled from "styled-components"; import { Button } from "components/button"; import { useState } from "react"; -import { API_BASE_URL } from "lib/common/animethemes/api"; +import { API_URL } from "lib/common/animethemes/api"; import { SEO } from "components/seo"; import getSharedPageProps from "utils/getSharedPageProps"; @@ -61,7 +61,7 @@ function Grill({ grill }) { } export async function getStaticProps() { - const res = await fetch(`${API_BASE_URL}/image?filter[facet]=Grill&page[size]=100`); + const res = await fetch(`${API_URL}/image?filter[facet]=Grill&page[size]=100`); const json = await res.json(); return { diff --git a/src/pages/profile/index.js b/src/pages/profile/index.js index 91a859d..214fb9e 100644 --- a/src/pages/profile/index.js +++ b/src/pages/profile/index.js @@ -5,17 +5,12 @@ import { Text } from "components/text"; import { SummaryCard, ThemeSummaryCard } from "components/card"; import { IconTextButton } from "components/button"; import { faKey, faTrash } from "@fortawesome/pro-solid-svg-icons"; -import { useWatchHistory } from "context/watchHistoryContext"; -import { useLocalPlaylist } from "context/localPlaylistContext"; +import useWatchHistory from "hooks/useWatchHistory"; +import useLocalPlaylist from "hooks/useLocalPlaylist"; import theme from "theme"; import { SearchFilter, SearchFilterGroup } from "components/search-filter"; import { Listbox } from "components/listbox"; -import { - DeveloperMode, - FeaturedThemePreview, - RevalidationToken, - ShowAnnouncements -} from "utils/settings"; +import { ColorTheme, DeveloperMode, FeaturedThemePreview, RevalidationToken, ShowAnnouncements } from "utils/settings"; import useSetting from "hooks/useSetting"; import { Input } from "components/input"; @@ -49,6 +44,7 @@ export default function ProfilePage() { const [featuredThemePreview, setFeaturedThemePreview] = useSetting(FeaturedThemePreview); const [developerMode, setDeveloperMode] = useSetting(DeveloperMode); const [revalidationToken, setRevalidationToken] = useSetting(RevalidationToken); + const [colorTheme, setColorTheme] = useSetting(ColorTheme); return ( <> @@ -62,6 +58,14 @@ export default function ProfilePage() { Settings + + Color Theme + + System + Dark + Light + + Show Announcements @@ -77,6 +81,8 @@ export default function ProfilePage() { Disabled + + Developer Mode @@ -88,7 +94,7 @@ export default function ProfilePage() { Revalidation Token diff --git a/src/pages/profile/playlist/index.js b/src/pages/profile/playlist/index.js index fd3c079..afa82b6 100644 --- a/src/pages/profile/playlist/index.js +++ b/src/pages/profile/playlist/index.js @@ -20,7 +20,7 @@ import { } from "utils/comparators"; import { SEO } from "components/seo"; import { Reorder } from "framer-motion"; -import { useLocalPlaylist } from "context/localPlaylistContext"; +import useLocalPlaylist from "hooks/useLocalPlaylist"; import theme from "theme"; import { MultiCoverImage } from "components/image"; import { faExclamationCircle } from "@fortawesome/pro-solid-svg-icons"; @@ -99,7 +99,7 @@ export default function PlaylistPage() { Local Playlist - theme?.anime)}/> + theme?.anime)}/> diff --git a/src/styles/global.js b/src/styles/global.js index 0f89171..601664e 100644 --- a/src/styles/global.js +++ b/src/styles/global.js @@ -8,16 +8,6 @@ export default createGlobalStyle` box-sizing: border-box; } - [theme="light"] { - ${createCssDefinition(colors)} - ${createCssDefinition(shadows)} - } - - [theme="dark"] { - ${createCssDefinition(darkColors)} - ${createCssDefinition(darkShadows)} - } - html { // Always show a vertical scroll bar, even if the page // is not scrollable to prevent layout shift. @@ -35,6 +25,26 @@ export default createGlobalStyle` -moz-osx-font-smoothing: grayscale; line-height: 1.5; + + &[theme="system"] { + ${createCssDefinition(darkColors)} + ${createCssDefinition(darkShadows)} + + @media (prefers-color-scheme: light) { + ${createCssDefinition(colors)} + ${createCssDefinition(shadows)} + } + } + + &[theme="light"] { + ${createCssDefinition(colors)} + ${createCssDefinition(shadows)} + } + + &[theme="dark"] { + ${createCssDefinition(darkColors)} + ${createCssDefinition(darkShadows)} + } } html, body, #__next { diff --git a/src/styles/mixins.js b/src/styles/mixins.js index 194b486..c90c3e0 100644 --- a/src/styles/mixins.js +++ b/src/styles/mixins.js @@ -1,20 +1,16 @@ import { css, keyframes } from "styled-components"; const loadingAnimationKeyframes = keyframes` - 0% { - background-position: 0 0; - } - 100% { - background-position: 100% 100%; - } + 50% { + background-color: rgba(46, 41, 58, 0.5); + } `; export const loadingAnimation = css` - background: radial-gradient(rgba(255, 255, 255, 0.05), rgba(255, 255, 255, 0.25)) no-repeat; - background-size: 500% 500%; + background-color: rgba(46, 41, 58, 1); @media (prefers-reduced-motion: no-preference) { - animation: ${loadingAnimationKeyframes} 2s infinite alternate linear; + animation: ${loadingAnimationKeyframes} 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; } `; diff --git a/src/theme/index.js b/src/theme/index.js index ee4bffc..0dd7b03 100644 --- a/src/theme/index.js +++ b/src/theme/index.js @@ -18,6 +18,9 @@ const theme = { toast: 20, menuOverlay: 2, menuPopover: 1 + }, + scalars: { + borderRadiusCard: "8px" } }; diff --git a/src/utils/config.js b/src/utils/config.js new file mode 100644 index 0000000..b04a22c --- /dev/null +++ b/src/utils/config.js @@ -0,0 +1,51 @@ +const { error, warn } = require("next/dist/build/output/log"); +const chalk = require("next/dist/lib/chalk").default; + +// Server-side + +const SERVER_API_URL = process.env.ANIMETHEMES_API_URL; +const SERVER_API_KEY = process.env.ANIMETHEMES_API_KEY; + +const REVALIDATE_TOKEN = process.env.REVALIDATE_TOKEN; +const ANALYZE = !!process.env.ANALYZE; + +// Server-side + Client-side + +const BASE_PATH = process.env.NEXT_PUBLIC_BASE_PATH ?? ""; + +const CLIENT_API_URL = process.env.NEXT_PUBLIC_API_URL; +const VIDEO_URL = process.env.NEXT_PUBLIC_VIDEO_URL; +const APP_URL = process.env.NEXT_PUBLIC_APP_URL; + +const STAGING = !!process.env.NEXT_PUBLIC_STAGING; + +function validateConfig() { + let isValid = true; + if (!SERVER_API_URL && !CLIENT_API_URL) { + error(`You need to either specify ${chalk.cyan("ANIMETHEMES_API_URL")} or ${chalk.cyan("NEXT_PUBLIC_API_URL")} for API requests to work.`); + isValid = false; + } + if (SERVER_API_URL && !CLIENT_API_URL) { + warn(`It is highly recommended to specify ${chalk.cyan("NEXT_PUBLIC_API_URL")}. Otherwise API on the client-side won't work.`); + } + if (!VIDEO_URL) { + warn(`It is recommended to specify ${chalk.cyan("NEXT_PUBLIC_VIDEO_URL")}. Otherwise videos won't play.`); + } + if (!APP_URL) { + warn(`You haven't specified ${chalk.cyan("NEXT_PUBLIC_APP_URL")}. This is fine for development, but should be fixed in production.`); + } + return isValid; +} + +module.exports = { + SERVER_API_URL, + SERVER_API_KEY, + REVALIDATE_TOKEN, + ANALYZE, + BASE_PATH, + CLIENT_API_URL, + VIDEO_URL, + APP_URL, + STAGING, + validateConfig, +}; diff --git a/src/utils/fetchStaticPaths.js b/src/utils/fetchStaticPaths.js index 81267b4..edd7b63 100644 --- a/src/utils/fetchStaticPaths.js +++ b/src/utils/fetchStaticPaths.js @@ -1,15 +1,15 @@ -export default async function fetchStaticPaths(fetchPaths) { - // In development all pages should be fetched on-demand. This speeds up page generation a lot. - if (process.env.NODE_ENV === "development") { +import { STAGING } from "utils/config"; + +export default async function fetchStaticPaths(fetchPaths, allPathsInStaging = false) { + // In development and staging all pages should be fetched on-demand. This speeds up page generation a lot. + if (process.env.NODE_ENV === "development" || (STAGING && !allPathsInStaging)) { return { paths: [], fallback: "blocking" }; } - const isStaging = !!process.env.NEXT_PUBLIC_STAGING; - - const paths = await fetchPaths(isStaging); + const paths = await fetchPaths(); return { paths, diff --git a/src/utils/settings.js b/src/utils/settings.js index aa9685c..7744065 100644 --- a/src/utils/settings.js +++ b/src/utils/settings.js @@ -27,5 +27,13 @@ export const RevalidationToken = Object.freeze({ export const GlobalVolume = Object.freeze({ __KEY__: "volume", - __INITIAL_VALUE__: 1 + __INITIAL_VALUE__: 1, +}); + +export const ColorTheme = Object.freeze({ + __KEY__: "theme", + __INITIAL_VALUE__: "system", + SYSTEM: "system", + DARK: "dark", + LIGHT: "light", }); diff --git a/src/utils/withBasePath.js b/src/utils/withBasePath.js index 87aacf8..42902a9 100644 --- a/src/utils/withBasePath.js +++ b/src/utils/withBasePath.js @@ -1,3 +1,5 @@ +import { BASE_PATH } from "utils/config"; + export default function withBasePath(path) { - return `${process.env.NEXT_PUBLIC_BASE_PATH || ""}${path}`; + return `${BASE_PATH}${path}`; }