diff --git a/apps/frontend/src/models/CardType.ts b/apps/frontend/src/models/CardType.ts index a6627731f6a28..92b90804ab20f 100644 --- a/apps/frontend/src/models/CardType.ts +++ b/apps/frontend/src/models/CardType.ts @@ -6,3 +6,30 @@ export const CardType = { WAKATIME: "wakatime", } as const; export type CardType = (typeof CardType)[keyof typeof CardType]; + +export const CardCategory = { + /** + * Cards describing a single repo or gist. + * They accept `show_owner` and use the `_repocard` theme variants. + */ + REPO: "repo", + /** + * Cards describing a user: stats, top languages and WakaTime. + */ + USER: "user", +} as const; +export type CardCategory = (typeof CardCategory)[keyof typeof CardCategory]; + +/** + * Category each card belongs to. + * + * Exhaustive by construction: a new {@link CardType} will not compile until it + * is categorised here, rather than silently falling into one of the branches. + */ +export const CATEGORY_BY_CARD_TYPE: Record = { + [CardType.STATS]: CardCategory.USER, + [CardType.TOP_LANGS]: CardCategory.USER, + [CardType.WAKATIME]: CardCategory.USER, + [CardType.PIN]: CardCategory.REPO, + [CardType.GIST]: CardCategory.REPO, +}; diff --git a/apps/frontend/src/pages/Home/Home.tsx b/apps/frontend/src/pages/Home/Home.tsx index 7c22adef91d09..52788559be50a 100644 --- a/apps/frontend/src/pages/Home/Home.tsx +++ b/apps/frontend/src/pages/Home/Home.tsx @@ -1,3 +1,4 @@ +import type { ThemeName } from "@stats-organization/github-readme-stats-core"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import type { JSX } from "react"; import { useDispatch } from "react-redux"; @@ -8,7 +9,11 @@ import { authenticate } from "../../api/user"; import { DEFAULT_OPTION as LANGUAGES_DEFAULT_LAYOUT } from "../../components/Home/LanguagesLayoutSection"; import { DEFAULT_OPTION as WAKATIME_DEFAULT_LAYOUT } from "../../components/Home/WakatimeLayoutSection"; import { DEMO_USER } from "../../constants"; -import { CardType } from "../../models/CardType"; +import { + CATEGORY_BY_CARD_TYPE, + CardCategory, + CardType, +} from "../../models/CardType"; import { STAGE_LABELS } from "../../models/Stage"; import type { StageIndex } from "../../models/Stage"; import { useTheme } from "../../redux/selectors/themeSelectors"; @@ -34,6 +39,13 @@ interface HomeScreenProps { setStage: (stageIndex: StageIndex) => void; } +function getDefaultTheme(isDark: boolean, category: CardCategory): ThemeName { + if (category === CardCategory.REPO) { + return isDark ? "dark_github_repocard" : "light_github_repocard"; + } + return isDark ? "dark_github" : "light_github"; +} + export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { const [isLoading, setIsLoading] = useState(false); @@ -66,7 +78,10 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { } const { isDark } = useTheme(); - const [theme, setTheme] = useState(isDark ? "dark" : "default"); + const cardCategory = CATEGORY_BY_CARD_TYPE[selectedCard]; + const [theme, setTheme] = useState(() => + getDefaultTheme(isDark, cardCategory), + ); const handleCardTypeChange = (cardType: CardType) => { if (cardType === CardType.TOP_LANGS) { @@ -83,12 +98,8 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { })); } - if (theme === "default" || theme === "default_repocard") { - if (cardType === CardType.PIN || cardType === CardType.GIST) { - setTheme("default_repocard"); - } else { - setTheme("default"); - } + if (theme === getDefaultTheme(isDark, cardCategory)) { + setTheme(getDefaultTheme(isDark, CATEGORY_BY_CARD_TYPE[cardType])); } setSelectedCard(cardType); @@ -107,19 +118,14 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { // Preview builder for the customize stage, dark-themed to match the surroundings. const customizeCardBuilder = useMemo( - () => (isDark ? cardBuilder.theme("github_dark") : cardBuilder), - [cardBuilder, isDark], + () => cardBuilder.theme(getDefaultTheme(isDark, cardCategory)), + [cardBuilder, isDark, cardCategory], ); // for stage four - const isRepoCard = - selectedCard === CardType.PIN || selectedCard === CardType.GIST; - const defaultTheme = isRepoCard ? "default_repocard" : "default"; - const isDefaultTheme = theme === defaultTheme; - const themeBuilder = useMemo( - () => (isDefaultTheme ? cardBuilder : cardBuilder.theme(theme)), - [cardBuilder, isDefaultTheme, theme], + () => cardBuilder.theme(theme), + [cardBuilder, theme], ); // for stage five @@ -252,6 +258,7 @@ export function HomeScreen({ stage, setStage }: HomeScreenProps): JSX.Element { { setTheme(theme); setStage(4); diff --git a/apps/frontend/src/pages/Home/stages/Customize.tsx b/apps/frontend/src/pages/Home/stages/Customize.tsx index 50c828ec55d04..3336aaeaf7ef3 100644 --- a/apps/frontend/src/pages/Home/stages/Customize.tsx +++ b/apps/frontend/src/pages/Home/stages/Customize.tsx @@ -13,7 +13,11 @@ import { DEMO_USER, DEMO_WAKATIME_USER, } from "../../../constants"; -import { CardType } from "../../../models/CardType"; +import { + CATEGORY_BY_CARD_TYPE, + CardCategory, + CardType, +} from "../../../models/CardType"; import type { CardUrlBuilder } from "../../../models/CardUrl"; import type { StageIndex } from "../../../models/Stage"; import { useIsAuthenticated } from "../../../redux/selectors/userSelectors"; @@ -367,7 +371,7 @@ export function CustomizeStage({ }} /> )} - {(cardType === CardType.PIN || cardType === CardType.GIST) && ( + {CATEGORY_BY_CARD_TYPE[cardType] === CardCategory.REPO && ( = [ + "default", + "default_repocard", + "github_dark", "merko", "blue-green", "gotham", @@ -18,29 +23,52 @@ const excludedThemes = [ "holi", ]; +const allThemeNames = Object.keys(themes) as Array; + // Light themes first, adaptive themes in the middle, dark themes last. -const themeList = Object.keys(themes) - .filter((myTheme) => !excludedThemes.includes(myTheme)) - .sort((a, b) => getThemeSortRank(a) - getThemeSortRank(b)); +const forPicker = (names: ReadonlyArray) => + names + .filter((name) => !excludedThemes.includes(name)) + .sort((a, b) => getThemeSortRank(a) - getThemeSortRank(b)); + +// Some themes come in pairs: `X_repocard` for the repo and gist cards, `X` for +// the stats, top languages and WakaTime cards. Each side offers only its own +// variant, so the theme Home.tsx starts on is always one of the entries here. +// +// Keep in sync with `generateTable` in `packages/core/scripts/generate-theme-readme.js`, +// which splits the theme README tables by the same rule. +const repoCardThemes = forPicker( + allThemeNames.filter( + (name) => !allThemeNames.includes(`${name}_repocard` as ThemeName), + ), +); + +const nonRepoCardThemes = forPicker( + allThemeNames.filter((name) => !name.endsWith("_repocard")), +); interface ThemeStageProps { card: CardUrlBuilder; theme: string; - onThemeChange: (theme: string) => void; + category: CardCategory; + onThemeChange: (theme: ThemeName) => void; } export function ThemeStage({ theme, card, + category, onThemeChange, }: ThemeStageProps): JSX.Element { const { isDark } = useTheme(); + const themeList = + category === CardCategory.REPO ? repoCardThemes : nonRepoCardThemes; return ( <>
{themeList.map((myTheme) => { - const themeColors = themes[myTheme as keyof typeof themes]; + const themeColors = themes[myTheme]; return (