Updating to Next.js 13 #64
|
@ -39,7 +39,8 @@
|
|||
- Support for Container Queries (media queries at the element level)
|
||||
- The website has a three-column layout, which turns into one-column + 2 toggleable side-menus if the screen is too narrow.
|
||||
- Check out our [Design System Showcase](https://accords-library.com/dev/showcase/design-system)
|
||||
- State Management: [React Context](https://reactjs.org/docs/context.html)
|
||||
- State Management: [Jōtai](https://jotai.org/)
|
||||
- Jōtai is a small-weighted library for atomic state management
|
||||
- Persistent app state using LocalStorage and SessionStorage
|
||||
- Accessibility
|
||||
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
|
||||
|
@ -56,7 +57,7 @@
|
|||
- The website is built before running in production
|
||||
- Performances are great, and possibility to deploy the app on a CDN
|
||||
- On-Demand ISR to continuously update the website when new content is added or existing content is modified/deleted
|
||||
- UI localizations are downloaded separetely into the `public/local-data` to avoid fetching the same static props for every page.
|
||||
- UI localizations are downloaded separetely into the `public/local-data` to avoid fetching the same static props for every page.
|
||||
- SEO
|
||||
- Good defaults for the metadata and OpenGraph properties
|
||||
- Each page can provide a custom thumbnail, title, description to be used
|
||||
|
@ -66,6 +67,7 @@
|
|||
- Each warning/error comes with a front-end link to the incriminating element, as well as a link to the CMS to fix it
|
||||
- Check for completeness, conformity, and integrity
|
||||
- Code quality and style
|
||||
|
||||
- React Strict Mode
|
||||
- [Eslint](https://www.npmjs.com/package/eslint) with [eslint-plugin-import](https://www.npmjs.com/package/eslint-plugin-import), [typescript-eslint](https://www.npmjs.com/package/@typescript-eslint/eslint-plugin)
|
||||
- [Prettier](https://www.npmjs.com/package/prettier) with [prettier-plugin-tailwindcss](https://www.npmjs.com/package/prettier-plugin-tailwindcss)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -26,6 +26,7 @@
|
|||
"@tippyjs/react": "^4.2.6",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"graphql-request": "^5.0.0",
|
||||
"jotai": "^1.9.0",
|
||||
"markdown-to-jsx": "^7.1.7",
|
||||
"next": "^13.0.1",
|
||||
"nodemailer": "^6.8.0",
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Ico, Icon } from "./Ico";
|
||||
import { ToolTip } from "./ToolTip";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -16,7 +17,7 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const AnchorShare = ({ id, className }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<ToolTip content={langui.copy_anchor_link} trigger="mouseenter" className="text-sm">
|
||||
<ToolTip content={langui.anchor_link_copied} trigger="click" className="text-sm">
|
||||
|
|
|
@ -7,11 +7,10 @@ import { MainPanel } from "./Panels/MainPanel";
|
|||
import { SafariPopup } from "./Panels/SafariPopup";
|
||||
import { isDefined, isUndefined } from "helpers/others";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
||||
import { Ids } from "types/ids";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -45,39 +44,34 @@ export const AppLayout = ({
|
|||
subPanelIcon = Icon.Tune,
|
||||
contentPanelScroolbar = true,
|
||||
}: Props): JSX.Element => {
|
||||
const {
|
||||
mainPanelOpen,
|
||||
mainPanelReduced,
|
||||
menuGestures,
|
||||
subPanelOpen,
|
||||
setMainPanelOpen,
|
||||
setSubPanelOpen,
|
||||
toggleMainPanelOpen,
|
||||
toggleSubPanelOpen,
|
||||
} = useAppLayout();
|
||||
const isMainPanelReduced = useAtomGetter(atoms.layout.mainPanelReduced);
|
||||
const [isSubPanelOpened, setSubPanelOpened] = useAtomPair(atoms.layout.subPanelOpened);
|
||||
const [isMainPanelOpened, setMainPanelOpened] = useAtomPair(atoms.layout.mainPanelOpened);
|
||||
const isMenuGesturesEnabled = useAtomGetter(atoms.layout.menuGesturesEnabled);
|
||||
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
|
||||
const { is1ColumnLayout, isScreenAtLeastXs } = useContainerQueries();
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
const isScreenAtLeastXs = useAtomGetter(atoms.containerQueries.isScreenAtLeastXs);
|
||||
|
||||
const handlers = useSwipeable({
|
||||
onSwipedLeft: (SwipeEventData) => {
|
||||
if (menuGestures) {
|
||||
if (isMenuGesturesEnabled) {
|
||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||
if (mainPanelOpen) {
|
||||
setMainPanelOpen(false);
|
||||
if (isMainPanelOpened) {
|
||||
setMainPanelOpened(false);
|
||||
} else if (isDefined(subPanel) && isDefined(contentPanel)) {
|
||||
setSubPanelOpen(true);
|
||||
setSubPanelOpened(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
onSwipedRight: (SwipeEventData) => {
|
||||
if (menuGestures) {
|
||||
if (isMenuGesturesEnabled) {
|
||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||
if (subPanelOpen) {
|
||||
setSubPanelOpen(false);
|
||||
if (isSubPanelOpened) {
|
||||
setSubPanelOpened(false);
|
||||
} else {
|
||||
setMainPanelOpen(true);
|
||||
setMainPanelOpened(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -99,7 +93,7 @@ export const AppLayout = ({
|
|||
style={{
|
||||
gridTemplateColumns: is1ColumnLayout
|
||||
? "1fr"
|
||||
: `${mainPanelReduced ? layout.mainMenuReduced : layout.mainMenu}rem ${
|
||||
: `${isMainPanelReduced ? layout.mainMenuReduced : layout.mainMenu}rem ${
|
||||
isDefined(subPanel) ? layout.subMenu : 0
|
||||
}rem 1fr`,
|
||||
}}>
|
||||
|
@ -128,7 +122,7 @@ export const AppLayout = ({
|
|||
`absolute inset-0 transition-filter duration-500
|
||||
[grid-area:content]`,
|
||||
cIf(
|
||||
(mainPanelOpen || subPanelOpen) && is1ColumnLayout,
|
||||
(isMainPanelOpened || isSubPanelOpened) && is1ColumnLayout,
|
||||
"z-10 backdrop-blur",
|
||||
"pointer-events-none touch-none"
|
||||
)
|
||||
|
@ -136,11 +130,15 @@ export const AppLayout = ({
|
|||
<div
|
||||
className={cJoin(
|
||||
"absolute inset-0 bg-shade transition-opacity duration-500",
|
||||
cIf((mainPanelOpen || subPanelOpen) && is1ColumnLayout, "opacity-60", "opacity-0")
|
||||
cIf(
|
||||
(isMainPanelOpened || isSubPanelOpened) && is1ColumnLayout,
|
||||
"opacity-60",
|
||||
"opacity-0"
|
||||
)
|
||||
)}
|
||||
onClick={() => {
|
||||
setMainPanelOpen(false);
|
||||
setSubPanelOpen(false);
|
||||
setMainPanelOpened(false);
|
||||
setSubPanelOpened(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
@ -175,7 +173,7 @@ export const AppLayout = ({
|
|||
"[grid-area:sub]"
|
||||
),
|
||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)] border-l"),
|
||||
cIf(is1ColumnLayout && !subPanelOpen && !turnSubIntoContent, "translate-x-[100vw]"),
|
||||
cIf(is1ColumnLayout && !isSubPanelOpened && !turnSubIntoContent, "translate-x-[100vw]"),
|
||||
cIf(is1ColumnLayout && turnSubIntoContent, "w-full border-l-0")
|
||||
)}>
|
||||
{subPanel}
|
||||
|
@ -189,7 +187,7 @@ export const AppLayout = ({
|
|||
transition-transform duration-300 scrollbar-none texture-paper-dots`,
|
||||
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
|
||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
|
||||
cIf(!mainPanelOpen && is1ColumnLayout, "-translate-x-full")
|
||||
cIf(!isMainPanelOpened && is1ColumnLayout, "-translate-x-full")
|
||||
)}>
|
||||
<MainPanel />
|
||||
</div>
|
||||
|
@ -202,11 +200,11 @@ export const AppLayout = ({
|
|||
cIf(!is1ColumnLayout, "hidden")
|
||||
)}>
|
||||
<Ico
|
||||
icon={mainPanelOpen ? Icon.Close : Icon.Menu}
|
||||
icon={isMainPanelOpened ? Icon.Close : Icon.Menu}
|
||||
className="cursor-pointer !text-2xl"
|
||||
onClick={() => {
|
||||
toggleMainPanelOpen();
|
||||
setSubPanelOpen(false);
|
||||
setMainPanelOpened((current) => !current);
|
||||
setSubPanelOpened(false);
|
||||
}}
|
||||
/>
|
||||
<p
|
||||
|
@ -220,11 +218,11 @@ export const AppLayout = ({
|
|||
</p>
|
||||
{isDefined(subPanel) && !turnSubIntoContent && (
|
||||
<Ico
|
||||
icon={subPanelOpen ? Icon.Close : subPanelIcon}
|
||||
icon={isSubPanelOpened ? Icon.Close : subPanelIcon}
|
||||
className="cursor-pointer !text-2xl"
|
||||
onClick={() => {
|
||||
toggleSubPanelOpen();
|
||||
setMainPanelOpen(false);
|
||||
setSubPanelOpened((current) => !current);
|
||||
setMainPanelOpened(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { useTerminalContext } from "contexts/TerminalContext";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomSetter, useAtomPair } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -31,9 +31,11 @@ export const Terminal = ({
|
|||
content,
|
||||
}: Props): JSX.Element => {
|
||||
const [childrenPaths, setChildrenPaths] = useState(propsChildrenPaths);
|
||||
const { darkMode, setPlayerName } = useUserSettings();
|
||||
const { previousCommands, previousLines, setPreviousCommands, setPreviousLines } =
|
||||
useTerminalContext();
|
||||
const setPlayerName = useAtomSetter(atoms.settings.playerName);
|
||||
|
||||
const [previousCommands, setPreviousCommands] = useAtomPair(atoms.terminal.previousCommands);
|
||||
const [previousLines, setPreviousLines] = useAtomPair(atoms.terminal.previousLines);
|
||||
|
||||
const [line, setLine] = useState("");
|
||||
const [displayCurrentLine, setDisplayCurrentLine] = useState(true);
|
||||
const [previousCommandIndex, setPreviousCommandIndex] = useState(0);
|
||||
|
@ -232,11 +234,7 @@ export const Terminal = ({
|
|||
}, [line]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
"h-screen overflow-hidden bg-light set-theme-font-standard",
|
||||
cIf(darkMode, "set-theme-dark", "set-theme-light")
|
||||
)}>
|
||||
<div className={cJoin("h-screen overflow-hidden bg-light set-theme-font-standard")}>
|
||||
<div
|
||||
ref={terminalWindowRef}
|
||||
className="h-full overflow-scroll scroll-auto p-6 scrollbar-none">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
|
@ -25,7 +26,7 @@ export const ContentPanel = ({
|
|||
children,
|
||||
className,
|
||||
}: Props): JSX.Element => {
|
||||
const { isContentPanelAtLeast3xl } = useContainerQueries();
|
||||
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
||||
return (
|
||||
<div className="grid h-full">
|
||||
<main
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { useEffect } from "react";
|
||||
import { useHotkeys } from "react-hotkeys-hook";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomSetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -27,13 +28,13 @@ export const Popup = ({
|
|||
hideBackground = false,
|
||||
padding = true,
|
||||
}: Props): JSX.Element => {
|
||||
const { setMenuGestures } = useAppLayout();
|
||||
const setMenuGesturesEnabled = useAtomSetter(atoms.layout.menuGesturesEnabled);
|
||||
|
||||
useHotkeys("escape", () => onCloseRequest?.(), {}, [onCloseRequest]);
|
||||
|
||||
useEffect(() => {
|
||||
setMenuGestures(!isVisible);
|
||||
}, [setMenuGestures, isVisible]);
|
||||
setMenuGesturesEnabled(!isVisible);
|
||||
}, [isVisible, setMenuGesturesEnabled]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
|
@ -13,7 +14,7 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const SubPanel = ({ children }: Props): JSX.Element => {
|
||||
const { isSubPanelAtLeastXs } = useContainerQueries();
|
||||
const isSubPanelAtLeastXs = useAtomGetter(atoms.containerQueries.isSubPanelAtLeastXs);
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
|
|
|
@ -23,8 +23,8 @@ export const UpPressable = ({
|
|||
href={href}
|
||||
onFocusChanged={setFocused}
|
||||
className={cJoin(
|
||||
`overflow-hidden rounded-md drop-shadow-lg transition-all duration-300 shadow-shade`,
|
||||
cIf(!noBackground, "bg-highlight"),
|
||||
`drop-shadow-lg transition-all duration-300 shadow-shade`,
|
||||
cIf(!noBackground, "overflow-hidden rounded-md bg-highlight"),
|
||||
cIf(
|
||||
disabled,
|
||||
"cursor-not-allowed opacity-50 grayscale",
|
||||
|
|
|
@ -6,7 +6,8 @@ import { cJoin } from "helpers/className";
|
|||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { iterateMap } from "helpers/others";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -32,7 +33,7 @@ export const LanguageSwitcher = ({
|
|||
onLanguageChanged,
|
||||
showBadge = true,
|
||||
}: Props): JSX.Element => {
|
||||
const { languages } = useLocalData();
|
||||
const languages = useAtomGetter(atoms.localData.languages);
|
||||
return (
|
||||
<ToolTip
|
||||
content={
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import router from "next/router";
|
||||
import { PointerEventHandler, useState } from "react";
|
||||
import { MouseEventHandler, useState } from "react";
|
||||
import { isDefined } from "helpers/others";
|
||||
|
||||
interface Props {
|
||||
|
@ -8,7 +8,7 @@ interface Props {
|
|||
allowNewTab?: boolean;
|
||||
alwaysNewTab?: boolean;
|
||||
children: React.ReactNode;
|
||||
onClick?: PointerEventHandler<HTMLDivElement>;
|
||||
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||
onFocusChanged?: (isFocused: boolean) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
@ -28,19 +28,19 @@ export const Link = ({
|
|||
return (
|
||||
<div
|
||||
className={className}
|
||||
onPointerLeave={() => {
|
||||
onMouseLeave={() => {
|
||||
setIsValidClick(false);
|
||||
onFocusChanged?.(false);
|
||||
}}
|
||||
onContextMenu={(event) => event.preventDefault()}
|
||||
onPointerDown={(event) => {
|
||||
onMouseDown={(event) => {
|
||||
if (!disabled) {
|
||||
event.preventDefault();
|
||||
onFocusChanged?.(true);
|
||||
setIsValidClick(true);
|
||||
}
|
||||
}}
|
||||
onPointerUp={(event) => {
|
||||
onMouseUp={(event) => {
|
||||
onFocusChanged?.(false);
|
||||
if (!disabled) {
|
||||
if (isDefined(onClick)) {
|
||||
|
|
|
@ -3,8 +3,9 @@ import { Button } from "components/Inputs/Button";
|
|||
import { ToolTip } from "components/ToolTip";
|
||||
import { LibraryItemUserStatus } from "types/types";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -20,7 +21,7 @@ interface Props {
|
|||
|
||||
export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
||||
const { libraryItemUserStatus, setLibraryItemUserStatus } = useLibraryItemUserStatus();
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
@ -10,7 +10,8 @@ import { Ids } from "types/ids";
|
|||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
interface Props {
|
||||
onCloseRequest: () => void;
|
||||
|
@ -138,7 +139,7 @@ const ControlButtons = ({
|
|||
onCloseRequest,
|
||||
toggleFullscreen,
|
||||
}: ControlButtonsProps): JSX.Element => {
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
|
||||
const PreviousButton = () => (
|
||||
<Button
|
||||
|
|
|
@ -13,10 +13,8 @@ import { AnchorShare } from "components/AnchorShare";
|
|||
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -31,10 +29,10 @@ interface MarkdawnProps {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Element => {
|
||||
const { playerName } = useUserSettings();
|
||||
const playerName = useAtomGetter(atoms.settings.playerName);
|
||||
const router = useRouter();
|
||||
const { isContentPanelAtLeastLg } = useContainerQueries();
|
||||
const { showLightBox } = useLightBox();
|
||||
const isContentPanelAtLeastLg = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastLg);
|
||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
const text = useMemo(
|
||||
|
@ -232,7 +230,7 @@ export const TableOfContents = ({
|
|||
horizontalLine = false,
|
||||
}: TableOfContentsProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const toc = useMemo(() => getTocFromMarkdawn(preprocessMarkDawn(text), title), [text, title]);
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { MouseEventHandler, useCallback, useMemo, useState } from "react";
|
||||
import { MouseEventHandler, useCallback, useMemo } from "react";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
@ -43,7 +43,6 @@ export const NavOption = ({
|
|||
() => active || router.asPath.startsWith(url),
|
||||
[active, router.asPath, url]
|
||||
);
|
||||
const [isFocused, setFocused] = useState(false);
|
||||
|
||||
return (
|
||||
<ToolTip
|
||||
|
@ -65,11 +64,8 @@ export const NavOption = ({
|
|||
border={border}
|
||||
onClick={onClick}
|
||||
active={isActive}
|
||||
disabled={disabled}
|
||||
onFocusChanged={setFocused}>
|
||||
{icon && (
|
||||
<Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive || isFocused} />
|
||||
)}
|
||||
disabled={disabled}>
|
||||
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive} />}
|
||||
{!reduced && (
|
||||
<div>
|
||||
<h3 className="text-2xl">{title}</h3>
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { useCallback } from "react";
|
||||
import { Icon } from "components/Ico";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -24,9 +23,8 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
||||
const { setSubPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
@ -34,12 +32,7 @@ export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props):
|
|||
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
||||
!isDefined(displayOnlyOn)) && (
|
||||
<div className={className}>
|
||||
<Button
|
||||
onClick={() => setSubPanelOpen(false)}
|
||||
href={href}
|
||||
text={`${langui.return_to} ${title}`}
|
||||
icon={Icon.NavigateBefore}
|
||||
/>
|
||||
<Button href={href} text={`${langui.return_to} ${title}`} icon={Icon.NavigateBefore} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
|
|
|
@ -3,15 +3,14 @@ import { HorizontalLine } from "components/HorizontalLine";
|
|||
import { Button } from "components/Inputs/Button";
|
||||
import { NavOption } from "components/PanelComponents/NavOption";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { Link } from "components/Inputs/Link";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { ColoredSvg } from "components/ColoredSvg";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter, useAtomPair, useAtomSetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -19,34 +18,35 @@ import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|||
*/
|
||||
|
||||
export const MainPanel = (): JSX.Element => {
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const { mainPanelReduced, toggleMainPanelReduced, setConfigPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [isMainPanelReduced, setMainPanelReduced] = useAtomPair(atoms.layout.mainPanelReduced);
|
||||
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cJoin(
|
||||
"grid content-start justify-center gap-y-2 p-8 text-center",
|
||||
cIf(mainPanelReduced && is3ColumnsLayout, "px-4")
|
||||
cIf(isMainPanelReduced && is3ColumnsLayout, "px-4")
|
||||
)}>
|
||||
{/* Reduce/expand main menu */}
|
||||
{is3ColumnsLayout && (
|
||||
<div
|
||||
className={cJoin(
|
||||
"fixed top-1/2",
|
||||
cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
|
||||
cIf(isMainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
|
||||
)}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (mainPanelReduced) {
|
||||
if (isMainPanelReduced) {
|
||||
sendAnalytics("MainPanel", "Expand");
|
||||
} else {
|
||||
sendAnalytics("MainPanel", "Reduce");
|
||||
}
|
||||
toggleMainPanelReduced();
|
||||
setMainPanelReduced((current) => !current);
|
||||
}}
|
||||
className="z-50 bg-light !px-2"
|
||||
icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft}
|
||||
icon={isMainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
@ -57,28 +57,28 @@ export const MainPanel = (): JSX.Element => {
|
|||
src="/icons/accords.svg"
|
||||
className={cJoin(
|
||||
"mb-4 aspect-square bg-black hover:bg-dark",
|
||||
cIf(mainPanelReduced && is3ColumnsLayout, "w-12", "w-1/2")
|
||||
cIf(isMainPanelReduced && is3ColumnsLayout, "w-12", "w-1/2")
|
||||
)}
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{(!mainPanelReduced || !is3ColumnsLayout) && (
|
||||
{(!isMainPanelReduced || !is3ColumnsLayout) && (
|
||||
<h2 className="mb-4 text-3xl">Accord’s Library</h2>
|
||||
)}
|
||||
|
||||
<div
|
||||
className={cJoin(
|
||||
"flex flex-wrap gap-2",
|
||||
cIf(mainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
||||
cIf(isMainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
||||
)}>
|
||||
<ToolTip
|
||||
content={<h3 className="text-2xl">{langui.open_settings}</h3>}
|
||||
placement="right"
|
||||
className="text-left"
|
||||
disabled={!mainPanelReduced}>
|
||||
disabled={!isMainPanelReduced}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setConfigPanelOpen(true);
|
||||
setSettingsOpened(true);
|
||||
sendAnalytics("Settings", "Open settings");
|
||||
}}
|
||||
icon={Icon.Settings}
|
||||
|
@ -95,7 +95,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
icon={Icon.LibraryBooks}
|
||||
title={langui.library}
|
||||
subtitle={langui.library_short_description}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
|
@ -103,7 +103,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
icon={Icon.Workspaces}
|
||||
title={langui.contents}
|
||||
subtitle={langui.contents_short_description}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
|
@ -111,7 +111,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
icon={Icon.TravelExplore}
|
||||
title={langui.wiki}
|
||||
subtitle={langui.wiki_short_description}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
|
@ -119,7 +119,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
icon={Icon.WatchLater}
|
||||
title={langui.chronicles}
|
||||
subtitle={langui.chronicles_short_description}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<HorizontalLine />
|
||||
|
@ -128,7 +128,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
url="/news"
|
||||
icon={Icon.Feed}
|
||||
title={langui.news}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
{/*
|
||||
|
@ -136,7 +136,7 @@ export const MainPanel = (): JSX.Element => {
|
|||
url="/merch"
|
||||
icon={Icon.Store}
|
||||
title={langui.merch}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
*/}
|
||||
|
||||
|
@ -144,26 +144,26 @@ export const MainPanel = (): JSX.Element => {
|
|||
url="https://gallery.accords-library.com/posts/"
|
||||
icon={Icon.Collections}
|
||||
title={langui.gallery}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
url="/archives"
|
||||
icon={Icon.Inventory2}
|
||||
title={langui.archives}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
url="/about-us"
|
||||
icon={Icon.Info}
|
||||
title={langui.about_us}
|
||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
||||
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||
/>
|
||||
|
||||
{(!mainPanelReduced || !is3ColumnsLayout) && <HorizontalLine />}
|
||||
{(!isMainPanelReduced || !is3ColumnsLayout) && <HorizontalLine />}
|
||||
|
||||
<div className={cJoin("text-center", cIf(mainPanelReduced && is3ColumnsLayout, "hidden"))}>
|
||||
<div className={cJoin("text-center", cIf(isMainPanelReduced && is3ColumnsLayout, "hidden"))}>
|
||||
{isDefinedAndNotEmpty(langui.licensing_notice) && (
|
||||
<p>
|
||||
<Markdown>{langui.licensing_notice}</Markdown>
|
||||
|
|
|
@ -7,37 +7,32 @@ import { OrderableList } from "components/Inputs/OrderableList";
|
|||
import { Select } from "components/Inputs/Select";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { Popup } from "components/Containers/Popup";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||
import { ThemeMode } from "contexts/settings";
|
||||
|
||||
export const SettingsPopup = (): JSX.Element => {
|
||||
const {
|
||||
currency,
|
||||
darkMode,
|
||||
dyslexic,
|
||||
fontSize,
|
||||
playerName,
|
||||
preferredLanguages,
|
||||
selectedThemeMode,
|
||||
setCurrency,
|
||||
setDarkMode,
|
||||
setDyslexic,
|
||||
setFontSize,
|
||||
setPlayerName,
|
||||
setPreferredLanguages,
|
||||
setSelectedThemeMode,
|
||||
} = useUserSettings();
|
||||
const [preferredLanguages, setPreferredLanguages] = useAtomPair(
|
||||
atoms.settings.preferredLanguages
|
||||
);
|
||||
const [isSettingsOpened, setSettingsOpened] = useAtomPair(atoms.layout.settingsOpened);
|
||||
const [currency, setCurrency] = useAtomPair(atoms.settings.currency);
|
||||
const [isDyslexic, setDyslexic] = useAtomPair(atoms.settings.dyslexic);
|
||||
const [fontSize, setFontSize] = useAtomPair(atoms.settings.fontSize);
|
||||
const [playerName, setPlayerName] = useAtomPair(atoms.settings.playerName);
|
||||
const [themeMode, setThemeMode] = useAtomPair(atoms.settings.themeMode);
|
||||
|
||||
const languages = useAtomGetter(atoms.localData.languages);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
|
||||
const { langui, currencies, languages } = useLocalData();
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const router = useRouter();
|
||||
const { configPanelOpen, setConfigPanelOpen } = useAppLayout();
|
||||
|
||||
const currencyOptions = useMemo(
|
||||
() =>
|
||||
|
@ -59,9 +54,9 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
|
||||
return (
|
||||
<Popup
|
||||
isVisible={configPanelOpen}
|
||||
isVisible={isSettingsOpened}
|
||||
onCloseRequest={() => {
|
||||
setConfigPanelOpen(false);
|
||||
setSettingsOpened(false);
|
||||
sendAnalytics("Settings", "Close settings");
|
||||
}}>
|
||||
<h2 className="text-2xl">{langui.settings}</h2>
|
||||
|
@ -110,28 +105,26 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
buttonsProps={[
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(false);
|
||||
setSelectedThemeMode(true);
|
||||
setThemeMode(ThemeMode.Light);
|
||||
sendAnalytics("Settings", "Change theme (light)");
|
||||
},
|
||||
active: selectedThemeMode && !darkMode,
|
||||
active: themeMode === ThemeMode.Light,
|
||||
text: langui.light,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setSelectedThemeMode(false);
|
||||
setThemeMode(ThemeMode.Auto);
|
||||
sendAnalytics("Settings", "Change theme (auto)");
|
||||
},
|
||||
active: !selectedThemeMode,
|
||||
active: themeMode === ThemeMode.Auto,
|
||||
text: langui.auto,
|
||||
},
|
||||
{
|
||||
onClick: () => {
|
||||
setDarkMode(true);
|
||||
setSelectedThemeMode(true);
|
||||
setThemeMode(ThemeMode.Dark);
|
||||
sendAnalytics("Settings", "Change theme (dark)");
|
||||
},
|
||||
active: selectedThemeMode && darkMode,
|
||||
active: themeMode === ThemeMode.Dark,
|
||||
text: langui.dark,
|
||||
},
|
||||
]}
|
||||
|
@ -198,7 +191,7 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
<h3 className="text-xl">{langui.font}</h3>
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
active={!dyslexic}
|
||||
active={!isDyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(false);
|
||||
sendAnalytics("Settings", "Change font (Zen Maru Gothic)");
|
||||
|
@ -207,7 +200,7 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
text="Zen Maru Gothic"
|
||||
/>
|
||||
<Button
|
||||
active={dyslexic}
|
||||
active={isDyslexic}
|
||||
onClick={() => {
|
||||
setDyslexic(true);
|
||||
sendAnalytics("Settings", "Change font (OpenDyslexic)");
|
||||
|
|
|
@ -13,7 +13,8 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|||
import { PostWithTranslations } from "types/types";
|
||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -48,7 +49,7 @@ export const PostPage = ({
|
|||
displayTitle = true,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: post.translations,
|
||||
languageExtractor: useCallback(
|
||||
|
|
|
@ -11,8 +11,8 @@ import { ImageQuality } from "helpers/img";
|
|||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -70,8 +70,8 @@ export const PreviewCard = ({
|
|||
infoAppend,
|
||||
disabled = false,
|
||||
}: Props): JSX.Element => {
|
||||
const { currency } = useUserSettings();
|
||||
const { currencies } = useLocalData();
|
||||
const currency = useAtomGetter(atoms.settings.currency);
|
||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||
const isHoverable = useDeviceSupportsHover();
|
||||
const router = useRouter();
|
||||
|
||||
|
|
|
@ -6,7 +6,8 @@ import { Chip } from "components/Chip";
|
|||
import { RecorderChipFragment } from "graphql/generated";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -21,7 +22,7 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
|
||||
return (
|
||||
<ToolTip
|
||||
|
|
|
@ -8,8 +8,8 @@ import { cJoin } from "helpers/className";
|
|||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { Ids } from "types/ids";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
interface Group<T> {
|
||||
name: string;
|
||||
|
@ -71,7 +71,7 @@ export const SmartList = <T,>({
|
|||
className,
|
||||
}: Props<T>): JSX.Element => {
|
||||
const [page, setPage] = useState(0);
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
useScrollTopOnChange(Ids.ContentPanel, [page], paginationScroolTop);
|
||||
useEffect(() => setPage(0), [searchingTerm, groupingFunction, groupSortingFunction, items]);
|
||||
|
||||
|
@ -228,8 +228,8 @@ export const SmartList = <T,>({
|
|||
*/
|
||||
|
||||
const DefaultRenderWhenEmpty = () => {
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<div className="grid h-full place-content-center">
|
||||
<div
|
||||
|
|
|
@ -6,8 +6,8 @@ import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
|||
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -42,8 +42,8 @@ export const ThumbnailHeader = ({
|
|||
description,
|
||||
languageSwitcher,
|
||||
}: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const { showLightBox } = useLightBox();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
@ -5,8 +5,8 @@ import { getStatusDescription } from "helpers/others";
|
|||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -30,8 +30,8 @@ interface Props {
|
|||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
||||
const { isContentPanelAtLeastMd } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeastMd = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastMd);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: translations,
|
||||
languageExtractor: useCallback((item: Props["translations"][number]) => item.language, []),
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { useStateWithLocalStorage } from "hooks/useStateWithLocalStorage";
|
||||
import { useScrollIntoView } from "hooks/useScrollIntoView";
|
||||
|
||||
interface AppLayoutState {
|
||||
subPanelOpen: boolean;
|
||||
toggleSubPanelOpen: () => void;
|
||||
setSubPanelOpen: Dispatch<SetStateAction<AppLayoutState["subPanelOpen"]>>;
|
||||
|
||||
configPanelOpen: boolean;
|
||||
toggleConfigPanelOpen: () => void;
|
||||
setConfigPanelOpen: Dispatch<SetStateAction<AppLayoutState["configPanelOpen"]>>;
|
||||
|
||||
mainPanelReduced: boolean;
|
||||
toggleMainPanelReduced: () => void;
|
||||
setMainPanelReduced: Dispatch<SetStateAction<AppLayoutState["mainPanelReduced"]>>;
|
||||
|
||||
mainPanelOpen: boolean;
|
||||
toggleMainPanelOpen: () => void;
|
||||
setMainPanelOpen: Dispatch<SetStateAction<AppLayoutState["mainPanelOpen"]>>;
|
||||
|
||||
menuGestures: boolean;
|
||||
toggleMenuGestures: () => void;
|
||||
setMenuGestures: Dispatch<SetStateAction<AppLayoutState["menuGestures"]>>;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<AppLayoutState> = {
|
||||
subPanelOpen: false,
|
||||
toggleSubPanelOpen: () => null,
|
||||
setSubPanelOpen: () => null,
|
||||
|
||||
configPanelOpen: false,
|
||||
setConfigPanelOpen: () => null,
|
||||
toggleConfigPanelOpen: () => null,
|
||||
|
||||
mainPanelReduced: false,
|
||||
setMainPanelReduced: () => null,
|
||||
toggleMainPanelReduced: () => null,
|
||||
|
||||
mainPanelOpen: false,
|
||||
toggleMainPanelOpen: () => null,
|
||||
setMainPanelOpen: () => null,
|
||||
|
||||
menuGestures: true,
|
||||
toggleMenuGestures: () => null,
|
||||
setMenuGestures: () => null,
|
||||
};
|
||||
|
||||
const AppLayoutContext = createContext<AppLayoutState>(initialState);
|
||||
|
||||
export const useAppLayout = (): AppLayoutState => useContext(AppLayoutContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const AppContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const router = useRouter();
|
||||
|
||||
const [subPanelOpen, setSubPanelOpen] = useStateWithLocalStorage(
|
||||
"subPanelOpen",
|
||||
initialState.subPanelOpen
|
||||
);
|
||||
|
||||
const [configPanelOpen, setConfigPanelOpen] = useLocalStorage(
|
||||
"configPanelOpen",
|
||||
initialState.configPanelOpen
|
||||
);
|
||||
|
||||
const [mainPanelReduced, setMainPanelReduced] = useLocalStorage(
|
||||
"mainPanelReduced",
|
||||
initialState.mainPanelReduced
|
||||
);
|
||||
|
||||
const [mainPanelOpen, setMainPanelOpen] = useStateWithLocalStorage(
|
||||
"mainPanelOpen",
|
||||
initialState.mainPanelOpen
|
||||
);
|
||||
|
||||
const [menuGestures, setMenuGestures] = useState(false);
|
||||
|
||||
const toggleSubPanelOpen = () => {
|
||||
setSubPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleConfigPanelOpen = () => {
|
||||
setConfigPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleMainPanelReduced = () => {
|
||||
setMainPanelReduced((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleMainPanelOpen = () => {
|
||||
setMainPanelOpen((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleMenuGestures = () => {
|
||||
setMenuGestures((current) => !current);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
router.events.on("routeChangeStart", () => {
|
||||
console.log("[Router Events] on routeChangeStart");
|
||||
setConfigPanelOpen(false);
|
||||
setMainPanelOpen(false);
|
||||
setSubPanelOpen(false);
|
||||
});
|
||||
|
||||
router.events.on("hashChangeStart", () => {
|
||||
console.log("[Router Events] on hashChangeStart");
|
||||
setSubPanelOpen(false);
|
||||
});
|
||||
}, [router.events, setConfigPanelOpen, setMainPanelOpen, setSubPanelOpen]);
|
||||
|
||||
useScrollIntoView();
|
||||
|
||||
return (
|
||||
<AppLayoutContext.Provider
|
||||
value={{
|
||||
subPanelOpen,
|
||||
configPanelOpen,
|
||||
mainPanelReduced,
|
||||
mainPanelOpen,
|
||||
menuGestures,
|
||||
setSubPanelOpen,
|
||||
setConfigPanelOpen,
|
||||
setMainPanelReduced,
|
||||
setMainPanelOpen,
|
||||
setMenuGestures,
|
||||
toggleSubPanelOpen,
|
||||
toggleConfigPanelOpen,
|
||||
toggleMainPanelReduced,
|
||||
toggleMainPanelOpen,
|
||||
toggleMenuGestures,
|
||||
}}>
|
||||
{children}
|
||||
</AppLayoutContext.Provider>
|
||||
);
|
||||
};
|
|
@ -1,315 +0,0 @@
|
|||
import React, { createContext, ReactNode, useContext, useMemo, useState } from "react";
|
||||
import { useUserSettings } from "./UserSettingsContext";
|
||||
import { useOnResize } from "hooks/useOnResize";
|
||||
import { Ids } from "types/ids";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
|
||||
type Size =
|
||||
| "2xl"
|
||||
| "2xs"
|
||||
| "3xl"
|
||||
| "4xl"
|
||||
| "5xl"
|
||||
| "6xl"
|
||||
| "7xl"
|
||||
| "lg"
|
||||
| "md"
|
||||
| "sm"
|
||||
| "xl"
|
||||
| "xs";
|
||||
|
||||
const sizes: Record<Size, number> = {
|
||||
"2xl": 42,
|
||||
"3xl": 48,
|
||||
"4xl": 56,
|
||||
"5xl": 64,
|
||||
"6xl": 72,
|
||||
"7xl": 80,
|
||||
lg: 32,
|
||||
md: 28,
|
||||
sm: 24,
|
||||
xl: 36,
|
||||
xs: 19,
|
||||
"2xs": 16,
|
||||
};
|
||||
|
||||
type ContainerQueriesContainers = "contentPanel" | "screen" | "subPanel";
|
||||
|
||||
type ContainerQueriesKeys =
|
||||
| "is1ColumnLayout"
|
||||
| "is3ColumnsLayout"
|
||||
| `is${Capitalize<ContainerQueriesContainers>}AtLeast${Capitalize<Size>}`;
|
||||
|
||||
type ContainerQueriesState = Record<ContainerQueriesKeys, boolean>;
|
||||
|
||||
const initialState: RequiredNonNullable<ContainerQueriesState> = {
|
||||
is1ColumnLayout: false,
|
||||
is3ColumnsLayout: false,
|
||||
isContentPanelAtLeast2xl: false,
|
||||
isContentPanelAtLeast2xs: false,
|
||||
isContentPanelAtLeast3xl: false,
|
||||
isContentPanelAtLeast4xl: false,
|
||||
isContentPanelAtLeast5xl: false,
|
||||
isContentPanelAtLeast6xl: false,
|
||||
isContentPanelAtLeast7xl: false,
|
||||
isContentPanelAtLeastLg: false,
|
||||
isContentPanelAtLeastMd: false,
|
||||
isContentPanelAtLeastSm: false,
|
||||
isContentPanelAtLeastXl: false,
|
||||
isContentPanelAtLeastXs: false,
|
||||
isScreenAtLeast2xl: false,
|
||||
isScreenAtLeast2xs: false,
|
||||
isScreenAtLeast3xl: false,
|
||||
isScreenAtLeast4xl: false,
|
||||
isScreenAtLeast5xl: false,
|
||||
isScreenAtLeast6xl: false,
|
||||
isScreenAtLeast7xl: false,
|
||||
isScreenAtLeastLg: false,
|
||||
isScreenAtLeastMd: false,
|
||||
isScreenAtLeastSm: false,
|
||||
isScreenAtLeastXl: false,
|
||||
isScreenAtLeastXs: false,
|
||||
isSubPanelAtLeast2xl: false,
|
||||
isSubPanelAtLeast2xs: false,
|
||||
isSubPanelAtLeast3xl: false,
|
||||
isSubPanelAtLeast4xl: false,
|
||||
isSubPanelAtLeast5xl: false,
|
||||
isSubPanelAtLeast6xl: false,
|
||||
isSubPanelAtLeast7xl: false,
|
||||
isSubPanelAtLeastLg: false,
|
||||
isSubPanelAtLeastMd: false,
|
||||
isSubPanelAtLeastSm: false,
|
||||
isSubPanelAtLeastXl: false,
|
||||
isSubPanelAtLeastXs: false,
|
||||
};
|
||||
|
||||
const ContainerQueriesContext = createContext<ContainerQueriesState>(initialState);
|
||||
|
||||
export const useContainerQueries = (): ContainerQueriesState => useContext(ContainerQueriesContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const ContainerQueriesContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const [screenWidth, setScreenWidth] = useState(0);
|
||||
const [contentPanelWidth, setContentPanelWidth] = useState(0);
|
||||
const [subPanelWidth, setSubPanelWidth] = useState(0);
|
||||
|
||||
useOnResize(Ids.Body, (width) => setScreenWidth(width));
|
||||
useOnResize(Ids.ContentPanel, (width) => setContentPanelWidth(width));
|
||||
useOnResize(Ids.SubPanel, (width) => setSubPanelWidth(width));
|
||||
|
||||
const { fontSize } = useUserSettings();
|
||||
|
||||
const screenAtLeasts = useMemo(
|
||||
() => ({
|
||||
isScreenAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isScreenAtLeastXs: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("xs")),
|
||||
isScreenAtLeastSm: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("sm")),
|
||||
isScreenAtLeastMd: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("md")),
|
||||
isScreenAtLeastLg: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("lg")),
|
||||
isScreenAtLeastXl: applyContainerQuery(fontSize, screenWidth, createAtLeastMediaQuery("xl")),
|
||||
isScreenAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isScreenAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isScreenAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isScreenAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isScreenAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isScreenAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
screenWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[screenWidth, fontSize]
|
||||
);
|
||||
|
||||
const columnLayouts = useMemo(
|
||||
() => ({
|
||||
is1ColumnLayout: !screenAtLeasts.isScreenAtLeast5xl,
|
||||
is3ColumnsLayout: screenAtLeasts.isScreenAtLeast5xl,
|
||||
}),
|
||||
[screenAtLeasts.isScreenAtLeast5xl]
|
||||
);
|
||||
|
||||
const subPanelAtLeasts = useMemo(
|
||||
() => ({
|
||||
isSubPanelAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isSubPanelAtLeastXs: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("xs")
|
||||
),
|
||||
isSubPanelAtLeastSm: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("sm")
|
||||
),
|
||||
isSubPanelAtLeastMd: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("md")
|
||||
),
|
||||
isSubPanelAtLeastLg: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("lg")
|
||||
),
|
||||
isSubPanelAtLeastXl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("xl")
|
||||
),
|
||||
isSubPanelAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isSubPanelAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isSubPanelAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isSubPanelAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isSubPanelAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isSubPanelAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
subPanelWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[subPanelWidth, fontSize]
|
||||
);
|
||||
|
||||
const contentPanelAtLeasts = useMemo(
|
||||
() => ({
|
||||
isContentPanelAtLeast2xs: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("2xs")
|
||||
),
|
||||
isContentPanelAtLeastXs: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("xs")
|
||||
),
|
||||
isContentPanelAtLeastSm: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("sm")
|
||||
),
|
||||
isContentPanelAtLeastMd: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("md")
|
||||
),
|
||||
isContentPanelAtLeastLg: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("lg")
|
||||
),
|
||||
isContentPanelAtLeastXl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("xl")
|
||||
),
|
||||
isContentPanelAtLeast2xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("2xl")
|
||||
),
|
||||
isContentPanelAtLeast3xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("3xl")
|
||||
),
|
||||
isContentPanelAtLeast4xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("4xl")
|
||||
),
|
||||
isContentPanelAtLeast5xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("5xl")
|
||||
),
|
||||
isContentPanelAtLeast6xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("6xl")
|
||||
),
|
||||
isContentPanelAtLeast7xl: applyContainerQuery(
|
||||
fontSize,
|
||||
contentPanelWidth,
|
||||
createAtLeastMediaQuery("7xl")
|
||||
),
|
||||
}),
|
||||
[contentPanelWidth, fontSize]
|
||||
);
|
||||
|
||||
return (
|
||||
<ContainerQueriesContext.Provider
|
||||
value={{
|
||||
...screenAtLeasts,
|
||||
...contentPanelAtLeasts,
|
||||
...subPanelAtLeasts,
|
||||
...columnLayouts,
|
||||
}}>
|
||||
{children}
|
||||
</ContainerQueriesContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
type MediaQuery = { value: number; unit: "px" | "rem"; rule: "max" | "min" };
|
||||
|
||||
const createAtLeastMediaQuery = (size: Size): MediaQuery => ({
|
||||
value: sizes[size],
|
||||
unit: "rem",
|
||||
rule: "min",
|
||||
});
|
||||
|
||||
const applyContainerQuery = (fontSize: number, width: number, query: MediaQuery): boolean => {
|
||||
const breakpoint = query.value * (query.unit === "rem" ? 16 : 1) * fontSize;
|
||||
return query.rule === "min" ? width >= breakpoint : width < breakpoint;
|
||||
};
|
|
@ -1,58 +0,0 @@
|
|||
import React, { createContext, ReactNode, useCallback, useContext, useState } from "react";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { LightBox } from "components/LightBox";
|
||||
import { filterDefined } from "helpers/others";
|
||||
|
||||
type LightBoxImagesNullable = (UploadImageFragment | string | null | undefined)[];
|
||||
type LightBoxImages = (UploadImageFragment | string)[];
|
||||
|
||||
interface LightBoxState {
|
||||
showLightBox: (images: LightBoxImagesNullable, index?: number) => void;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<LightBoxState> = {
|
||||
showLightBox: () => null,
|
||||
};
|
||||
|
||||
const LightBoxContext = createContext<LightBoxState>(initialState);
|
||||
|
||||
export const useLightBox = (): LightBoxState => useContext(LightBoxContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const LightBoxContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const [isLightBoxVisible, setLightBoxVisibility] = useState(false);
|
||||
const [lightBoxImages, setLightBoxImages] = useState<LightBoxImages>([]);
|
||||
const [lightBoxIndex, setLightBoxIndex] = useState(0);
|
||||
|
||||
const showLightBox = useCallback((images: LightBoxImagesNullable, index = 0) => {
|
||||
const filteredImages = filterDefined(images);
|
||||
setLightBoxIndex(index);
|
||||
setLightBoxImages(filteredImages);
|
||||
setLightBoxVisibility(true);
|
||||
}, []);
|
||||
|
||||
const closeLightBox = useCallback(() => {
|
||||
setLightBoxVisibility(false);
|
||||
setTimeout(() => setLightBoxImages([]), 100);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LightBoxContext.Provider value={{ showLightBox }}>
|
||||
<LightBox
|
||||
isVisible={isLightBoxVisible}
|
||||
onCloseRequest={closeLightBox}
|
||||
image={lightBoxImages[lightBoxIndex]}
|
||||
isNextImageAvailable={lightBoxIndex < lightBoxImages.length - 1}
|
||||
isPreviousImageAvailable={lightBoxIndex > 0}
|
||||
onPressNext={() => setLightBoxIndex((current) => current + 1)}
|
||||
onPressPrevious={() => setLightBoxIndex((current) => current - 1)}
|
||||
/>
|
||||
|
||||
{children}
|
||||
</LightBoxContext.Provider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,54 @@
|
|||
import React, { useCallback, useState } from "react";
|
||||
import { useEffectOnce } from "usehooks-ts";
|
||||
import { atom } from "jotai";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { LightBox } from "components/LightBox";
|
||||
import { filterDefined } from "helpers/others";
|
||||
import { atomPairing, useAtomSetter } from "helpers/atoms";
|
||||
|
||||
const lightBoxAtom = atomPairing(
|
||||
atom<{
|
||||
showLightBox: (
|
||||
images: (UploadImageFragment | string | null | undefined)[],
|
||||
index?: number
|
||||
) => void;
|
||||
}>({ showLightBox: () => null })
|
||||
);
|
||||
|
||||
export const lightBox = lightBoxAtom[0];
|
||||
|
||||
export const LightBoxProvider = (): JSX.Element => {
|
||||
const [isLightBoxVisible, setLightBoxVisibility] = useState(false);
|
||||
const [lightBoxImages, setLightBoxImages] = useState<(UploadImageFragment | string)[]>([]);
|
||||
const [lightBoxIndex, setLightBoxIndex] = useState(0);
|
||||
|
||||
const setShowLightBox = useAtomSetter(lightBoxAtom);
|
||||
|
||||
useEffectOnce(() =>
|
||||
setShowLightBox({
|
||||
showLightBox: (images, index = 0) => {
|
||||
const filteredImages = filterDefined(images);
|
||||
setLightBoxIndex(index);
|
||||
setLightBoxImages(filteredImages);
|
||||
setLightBoxVisibility(true);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
const closeLightBox = useCallback(() => {
|
||||
setLightBoxVisibility(false);
|
||||
setTimeout(() => setLightBoxImages([]), 100);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<LightBox
|
||||
isVisible={isLightBoxVisible}
|
||||
onCloseRequest={closeLightBox}
|
||||
image={lightBoxImages[lightBoxIndex]}
|
||||
isNextImageAvailable={lightBoxIndex < lightBoxImages.length - 1}
|
||||
isPreviousImageAvailable={lightBoxIndex > 0}
|
||||
onPressNext={() => setLightBoxIndex((current) => current + 1)}
|
||||
onPressPrevious={() => setLightBoxIndex((current) => current - 1)}
|
||||
/>
|
||||
);
|
||||
};
|
|
@ -1,70 +0,0 @@
|
|||
import React, { createContext, ReactNode, useContext, useMemo } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { useFetch } from "usehooks-ts";
|
||||
import {
|
||||
Langui,
|
||||
Languages,
|
||||
Currencies,
|
||||
processCurrencies,
|
||||
processLanguages,
|
||||
processLangui,
|
||||
} from "helpers/localData";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { LocalDataFile } from "graphql/fetchLocalData";
|
||||
import {
|
||||
LocalDataGetCurrenciesQuery,
|
||||
LocalDataGetLanguagesQuery,
|
||||
LocalDataGetWebsiteInterfacesQuery,
|
||||
} from "graphql/generated";
|
||||
|
||||
interface LocalDataState {
|
||||
langui: Langui;
|
||||
languages: Languages;
|
||||
currencies: Currencies;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<LocalDataState> = {
|
||||
currencies: [],
|
||||
languages: [],
|
||||
langui: {},
|
||||
};
|
||||
|
||||
const LocalDataContext = createContext<LocalDataState>(initialState);
|
||||
|
||||
export const useLocalData = (): LocalDataState => useContext(LocalDataContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const LocalDataContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const langui = useLangui();
|
||||
const languages = useLanguages();
|
||||
const currencies = useCurrencies();
|
||||
|
||||
return (
|
||||
<LocalDataContext.Provider value={{ langui, languages, currencies }}>
|
||||
{children}
|
||||
</LocalDataContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
|
||||
|
||||
const useLangui = (): Langui => {
|
||||
const { locale } = useRouter();
|
||||
const { data: websiteInterfaces } = useFetch<LocalDataGetWebsiteInterfacesQuery>(
|
||||
getFileName("websiteInterfaces")
|
||||
);
|
||||
return useMemo(() => processLangui(websiteInterfaces, locale), [websiteInterfaces, locale]);
|
||||
};
|
||||
|
||||
const useCurrencies = (): Currencies => {
|
||||
const { data: currencies } = useFetch<LocalDataGetCurrenciesQuery>(getFileName("currencies"));
|
||||
return useMemo(() => processCurrencies(currencies), [currencies]);
|
||||
};
|
||||
|
||||
const useLanguages = (): Languages => {
|
||||
const { data: languages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
|
||||
return useMemo(() => processLanguages(languages), [languages]);
|
||||
};
|
|
@ -1,42 +0,0 @@
|
|||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useState,
|
||||
} from "react";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
|
||||
interface TerminalState {
|
||||
previousLines: string[];
|
||||
previousCommands: string[];
|
||||
setPreviousLines: Dispatch<SetStateAction<TerminalState["previousLines"]>>;
|
||||
setPreviousCommands: Dispatch<SetStateAction<TerminalState["previousCommands"]>>;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<TerminalState> = {
|
||||
previousLines: [],
|
||||
previousCommands: [],
|
||||
setPreviousLines: () => null,
|
||||
setPreviousCommands: () => null,
|
||||
};
|
||||
|
||||
const TerminalContext = createContext<TerminalState>(initialState);
|
||||
|
||||
export const useTerminalContext = (): TerminalState => useContext(TerminalContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const TerminalContextProvider = ({ children }: Props): JSX.Element => {
|
||||
const [previousLines, setPreviousLines] = useState(initialState.previousLines);
|
||||
const [previousCommands, setPreviousCommands] = useState(initialState.previousCommands);
|
||||
return (
|
||||
<TerminalContext.Provider
|
||||
value={{ previousCommands, previousLines, setPreviousCommands, setPreviousLines }}>
|
||||
{children}
|
||||
</TerminalContext.Provider>
|
||||
);
|
||||
};
|
|
@ -1,189 +0,0 @@
|
|||
import { useLocalStorage } from "usehooks-ts";
|
||||
import React, {
|
||||
createContext,
|
||||
Dispatch,
|
||||
ReactNode,
|
||||
SetStateAction,
|
||||
useContext,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
} from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { RequiredNonNullable } from "types/types";
|
||||
import { getDefaultPreferredLanguages } from "helpers/locales";
|
||||
import { useDarkMode } from "hooks/useDarkMode";
|
||||
import { SettingsPopup } from "components/Panels/SettingsPopup";
|
||||
|
||||
interface UserSettingsState {
|
||||
fontSize: number;
|
||||
setFontSize: Dispatch<SetStateAction<UserSettingsState["fontSize"]>>;
|
||||
|
||||
darkMode: boolean;
|
||||
toggleDarkMode: () => void;
|
||||
setDarkMode: Dispatch<SetStateAction<UserSettingsState["darkMode"]>>;
|
||||
|
||||
selectedThemeMode: boolean;
|
||||
toggleSelectedThemeMode: () => void;
|
||||
setSelectedThemeMode: Dispatch<SetStateAction<UserSettingsState["selectedThemeMode"]>>;
|
||||
|
||||
dyslexic: boolean;
|
||||
toggleDyslexic: () => void;
|
||||
setDyslexic: Dispatch<SetStateAction<UserSettingsState["dyslexic"]>>;
|
||||
|
||||
currency: string;
|
||||
setCurrency: Dispatch<SetStateAction<UserSettingsState["currency"]>>;
|
||||
|
||||
playerName: string;
|
||||
setPlayerName: Dispatch<SetStateAction<UserSettingsState["playerName"]>>;
|
||||
|
||||
preferredLanguages: string[];
|
||||
setPreferredLanguages: Dispatch<SetStateAction<UserSettingsState["preferredLanguages"]>>;
|
||||
}
|
||||
|
||||
const initialState: RequiredNonNullable<UserSettingsState> = {
|
||||
fontSize: 1,
|
||||
setFontSize: () => null,
|
||||
|
||||
darkMode: false,
|
||||
toggleDarkMode: () => null,
|
||||
setDarkMode: () => null,
|
||||
|
||||
selectedThemeMode: false,
|
||||
toggleSelectedThemeMode: () => null,
|
||||
setSelectedThemeMode: () => null,
|
||||
|
||||
dyslexic: false,
|
||||
toggleDyslexic: () => null,
|
||||
setDyslexic: () => null,
|
||||
|
||||
currency: "USD",
|
||||
setCurrency: () => null,
|
||||
|
||||
playerName: "",
|
||||
setPlayerName: () => null,
|
||||
|
||||
preferredLanguages: [],
|
||||
setPreferredLanguages: () => null,
|
||||
};
|
||||
|
||||
const UserSettingsContext = createContext<UserSettingsState>(initialState);
|
||||
|
||||
export const useUserSettings = (): UserSettingsState => useContext(UserSettingsContext);
|
||||
|
||||
interface Props {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const UserSettingsProvider = ({ children }: Props): JSX.Element => {
|
||||
const router = useRouter();
|
||||
|
||||
const [fontSize, setFontSize] = useLocalStorage("fontSize", initialState.fontSize);
|
||||
|
||||
const [darkMode, selectedThemeMode, setDarkMode, setSelectedThemeMode] = useDarkMode(
|
||||
"darkMode",
|
||||
initialState.darkMode
|
||||
);
|
||||
|
||||
const [dyslexic, setDyslexic] = useLocalStorage("dyslexic", initialState.dyslexic);
|
||||
|
||||
const [currency, setCurrency] = useLocalStorage("currency", initialState.currency);
|
||||
|
||||
const [playerName, setPlayerName] = useLocalStorage("playerName", initialState.playerName);
|
||||
|
||||
const [preferredLanguages, setPreferredLanguages] = useLocalStorage(
|
||||
"preferredLanguages",
|
||||
initialState.preferredLanguages
|
||||
);
|
||||
|
||||
const toggleDarkMode = () => {
|
||||
setDarkMode((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleSelectedThemeMode = () => {
|
||||
setSelectedThemeMode((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
const toggleDyslexic = () => {
|
||||
setDyslexic((current) => (isDefined(current) ? !current : current));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (preferredLanguages.length === 0) {
|
||||
if (isDefinedAndNotEmpty(router.locale) && router.locales) {
|
||||
setPreferredLanguages(getDefaultPreferredLanguages(router.locale, router.locales));
|
||||
}
|
||||
} else if (router.locale !== preferredLanguages[0]) {
|
||||
/*
|
||||
* Using a timeout to the code getting stuck into a loop when reaching the website with a
|
||||
* different preferredLanguages[0] from router.locale
|
||||
*/
|
||||
setTimeout(
|
||||
async () =>
|
||||
router.replace(router.asPath, router.asPath, {
|
||||
locale: preferredLanguages[0],
|
||||
}),
|
||||
250
|
||||
);
|
||||
}
|
||||
}, [preferredLanguages, router, router.locale, router.locales, setPreferredLanguages]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const html = document.getElementsByTagName("html")[0];
|
||||
if (isDefined(html)) {
|
||||
html.style.fontSize = `${fontSize * 100}%`;
|
||||
}
|
||||
}, [fontSize]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (darkMode) {
|
||||
next.classList.add("set-theme-dark");
|
||||
next.classList.remove("set-theme-light");
|
||||
} else {
|
||||
next.classList.add("set-theme-light");
|
||||
next.classList.remove("set-theme-dark");
|
||||
}
|
||||
}
|
||||
}, [darkMode]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (dyslexic) {
|
||||
next.classList.add("set-theme-font-dyslexic");
|
||||
next.classList.remove("set-theme-font-standard");
|
||||
} else {
|
||||
next.classList.add("set-theme-font-standard");
|
||||
next.classList.remove("set-theme-font-dyslexic");
|
||||
}
|
||||
}
|
||||
}, [dyslexic]);
|
||||
|
||||
return (
|
||||
<UserSettingsContext.Provider
|
||||
value={{
|
||||
fontSize,
|
||||
darkMode,
|
||||
selectedThemeMode,
|
||||
dyslexic,
|
||||
currency,
|
||||
playerName,
|
||||
preferredLanguages,
|
||||
setFontSize,
|
||||
setDarkMode,
|
||||
setSelectedThemeMode,
|
||||
setDyslexic,
|
||||
setCurrency,
|
||||
setPlayerName,
|
||||
setPreferredLanguages,
|
||||
toggleDarkMode,
|
||||
toggleSelectedThemeMode,
|
||||
toggleDyslexic,
|
||||
}}>
|
||||
<SettingsPopup />
|
||||
{children}
|
||||
</UserSettingsContext.Provider>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,29 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import { useScrollIntoView } from "hooks/useScrollIntoView";
|
||||
import { useAtomSetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
export const useAppLayout = (): void => {
|
||||
const router = useRouter();
|
||||
|
||||
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
|
||||
const setMainPanelOpened = useAtomSetter(atoms.layout.mainPanelOpened);
|
||||
const setSubPanelOpened = useAtomSetter(atoms.layout.subPanelOpened);
|
||||
|
||||
useEffect(() => {
|
||||
router.events.on("routeChangeStart", () => {
|
||||
console.log("[Router Events] on routeChangeStart");
|
||||
setSettingsOpened(false);
|
||||
setMainPanelOpened(false);
|
||||
setSubPanelOpened(false);
|
||||
});
|
||||
|
||||
router.events.on("hashChangeStart", () => {
|
||||
console.log("[Router Events] on hashChangeStart");
|
||||
setSubPanelOpened(false);
|
||||
});
|
||||
}, [router, setSettingsOpened, setMainPanelOpened, setSubPanelOpened]);
|
||||
|
||||
useScrollIntoView();
|
||||
};
|
|
@ -0,0 +1,49 @@
|
|||
import { atom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { localData } from "contexts/localData";
|
||||
import { containerQueries } from "contexts/containerQueries";
|
||||
import { atomPairing } from "helpers/atoms";
|
||||
import { settings } from "contexts/settings";
|
||||
import { lightBox } from "contexts/LightBoxProvider";
|
||||
|
||||
/*
|
||||
* I'm getting a weird error if I put those atoms in appLayout.ts
|
||||
* So I'm putting the atoms here. Sucks, I know.
|
||||
*/
|
||||
|
||||
/* [ APPLAYOUT ATOMS ] */
|
||||
|
||||
const mainPanelReduced = atomPairing(atomWithStorage("isMainPanelReduced", false));
|
||||
const settingsOpened = atomPairing(atomWithStorage("isSettingsOpened", false));
|
||||
const subPanelOpened = atomPairing(atomWithStorage("isSubPanelOpened", false));
|
||||
const mainPanelOpened = atomPairing(atomWithStorage("isMainPanelOpened", false));
|
||||
const menuGesturesEnabled = atomPairing(atomWithStorage("isMenuGesturesEnabled", false));
|
||||
const terminalMode = atom((get) => get(settings.playerName[0]) === "root");
|
||||
|
||||
const layout = {
|
||||
mainPanelReduced,
|
||||
settingsOpened,
|
||||
subPanelOpened,
|
||||
mainPanelOpened,
|
||||
menuGesturesEnabled,
|
||||
terminalMode,
|
||||
};
|
||||
|
||||
/* [ TERMINAL ATOMS ] */
|
||||
|
||||
const previousLines = atomPairing(atom<string[]>([]));
|
||||
const previousCommands = atomPairing(atom<string[]>([]));
|
||||
|
||||
const terminal = {
|
||||
previousLines,
|
||||
previousCommands,
|
||||
};
|
||||
|
||||
export const atoms = {
|
||||
settings,
|
||||
layout,
|
||||
terminal,
|
||||
localData,
|
||||
lightBox,
|
||||
containerQueries,
|
||||
};
|
|
@ -0,0 +1,146 @@
|
|||
import { atom, Getter } from "jotai";
|
||||
import { atomPairing, useAtomSetter } from "helpers/atoms";
|
||||
import { useOnResize } from "hooks/useOnResize";
|
||||
import { Ids } from "types/ids";
|
||||
import { settings } from "contexts/settings";
|
||||
|
||||
type Size =
|
||||
| "2xl"
|
||||
| "2xs"
|
||||
| "3xl"
|
||||
| "4xl"
|
||||
| "5xl"
|
||||
| "6xl"
|
||||
| "7xl"
|
||||
| "lg"
|
||||
| "md"
|
||||
| "sm"
|
||||
| "xl"
|
||||
| "xs";
|
||||
|
||||
const sizes: Record<Size, number> = {
|
||||
"2xl": 42,
|
||||
"3xl": 48,
|
||||
"4xl": 56,
|
||||
"5xl": 64,
|
||||
"6xl": 72,
|
||||
"7xl": 80,
|
||||
lg: 32,
|
||||
md: 28,
|
||||
sm: 24,
|
||||
xl: 36,
|
||||
xs: 19,
|
||||
"2xs": 16,
|
||||
};
|
||||
|
||||
const isAtLeastContainerQuery = (width: number, size: Size, fontSize: number): boolean =>
|
||||
width >= sizes[size] * 16 * fontSize;
|
||||
|
||||
const screenWidth = atomPairing(atom(0));
|
||||
const contentPanelWidth = atomPairing(atom(0));
|
||||
const subPanelWidth = atomPairing(atom(0));
|
||||
|
||||
const screenGetter = (get: Getter, size: Size) =>
|
||||
isAtLeastContainerQuery(get(screenWidth[0]), size, get(settings.fontSize[0]));
|
||||
|
||||
const isScreenAtLeast2xs = atom((get) => screenGetter(get, "2xs"));
|
||||
const isScreenAtLeastXs = atom((get) => screenGetter(get, "xs"));
|
||||
const isScreenAtLeastSm = atom((get) => screenGetter(get, "sm"));
|
||||
const isScreenAtLeastMd = atom((get) => screenGetter(get, "md"));
|
||||
const isScreenAtLeastLg = atom((get) => screenGetter(get, "lg"));
|
||||
const isScreenAtLeastXl = atom((get) => screenGetter(get, "xl"));
|
||||
const isScreenAtLeast2xl = atom((get) => screenGetter(get, "2xl"));
|
||||
const isScreenAtLeast3xl = atom((get) => screenGetter(get, "3xl"));
|
||||
const isScreenAtLeast4xl = atom((get) => screenGetter(get, "4xl"));
|
||||
const isScreenAtLeast5xl = atom((get) => screenGetter(get, "5xl"));
|
||||
const isScreenAtLeast6xl = atom((get) => screenGetter(get, "6xl"));
|
||||
const isScreenAtLeast7xl = atom((get) => screenGetter(get, "7xl"));
|
||||
|
||||
const is1ColumnLayout = atom((get) => !get(isScreenAtLeast5xl));
|
||||
const is3ColumnsLayout = atom((get) => get(isScreenAtLeast5xl));
|
||||
|
||||
const contentPanelGetter = (get: Getter, size: Size) =>
|
||||
isAtLeastContainerQuery(get(contentPanelWidth[0]), size, get(settings.fontSize[0]));
|
||||
|
||||
const isContentPanelAtLeast2xs = atom((get) => contentPanelGetter(get, "2xs"));
|
||||
const isContentPanelAtLeastXs = atom((get) => contentPanelGetter(get, "xs"));
|
||||
const isContentPanelAtLeastSm = atom((get) => contentPanelGetter(get, "sm"));
|
||||
const isContentPanelAtLeastMd = atom((get) => contentPanelGetter(get, "md"));
|
||||
const isContentPanelAtLeastLg = atom((get) => contentPanelGetter(get, "lg"));
|
||||
const isContentPanelAtLeastXl = atom((get) => contentPanelGetter(get, "xl"));
|
||||
const isContentPanelAtLeast2xl = atom((get) => contentPanelGetter(get, "2xl"));
|
||||
const isContentPanelAtLeast3xl = atom((get) => contentPanelGetter(get, "3xl"));
|
||||
const isContentPanelAtLeast4xl = atom((get) => contentPanelGetter(get, "4xl"));
|
||||
const isContentPanelAtLeast5xl = atom((get) => contentPanelGetter(get, "5xl"));
|
||||
const isContentPanelAtLeast6xl = atom((get) => contentPanelGetter(get, "6xl"));
|
||||
const isContentPanelAtLeast7xl = atom((get) => contentPanelGetter(get, "7xl"));
|
||||
|
||||
const subPanelGetter = (get: Getter, size: Size) =>
|
||||
isAtLeastContainerQuery(get(subPanelWidth[0]), size, get(settings.fontSize[0]));
|
||||
|
||||
const isSubPanelAtLeast2xs = atom((get) => subPanelGetter(get, "2xs"));
|
||||
const isSubPanelAtLeastXs = atom((get) => subPanelGetter(get, "xs"));
|
||||
const isSubPanelAtLeastSm = atom((get) => subPanelGetter(get, "sm"));
|
||||
const isSubPanelAtLeastMd = atom((get) => subPanelGetter(get, "md"));
|
||||
const isSubPanelAtLeastLg = atom((get) => subPanelGetter(get, "lg"));
|
||||
const isSubPanelAtLeastXl = atom((get) => subPanelGetter(get, "xl"));
|
||||
const isSubPanelAtLeast2xl = atom((get) => subPanelGetter(get, "2xl"));
|
||||
const isSubPanelAtLeast3xl = atom((get) => subPanelGetter(get, "3xl"));
|
||||
const isSubPanelAtLeast4xl = atom((get) => subPanelGetter(get, "4xl"));
|
||||
const isSubPanelAtLeast5xl = atom((get) => subPanelGetter(get, "5xl"));
|
||||
const isSubPanelAtLeast6xl = atom((get) => subPanelGetter(get, "6xl"));
|
||||
const isSubPanelAtLeast7xl = atom((get) => subPanelGetter(get, "7xl"));
|
||||
|
||||
export const containerQueries = {
|
||||
is1ColumnLayout,
|
||||
is3ColumnsLayout,
|
||||
|
||||
isScreenAtLeast2xs,
|
||||
isScreenAtLeastXs,
|
||||
isScreenAtLeastSm,
|
||||
isScreenAtLeastMd,
|
||||
isScreenAtLeastLg,
|
||||
isScreenAtLeastXl,
|
||||
isScreenAtLeast2xl,
|
||||
isScreenAtLeast3xl,
|
||||
isScreenAtLeast4xl,
|
||||
isScreenAtLeast5xl,
|
||||
isScreenAtLeast6xl,
|
||||
isScreenAtLeast7xl,
|
||||
|
||||
isContentPanelAtLeast2xs,
|
||||
isContentPanelAtLeastXs,
|
||||
isContentPanelAtLeastSm,
|
||||
isContentPanelAtLeastMd,
|
||||
isContentPanelAtLeastLg,
|
||||
isContentPanelAtLeastXl,
|
||||
isContentPanelAtLeast2xl,
|
||||
isContentPanelAtLeast3xl,
|
||||
isContentPanelAtLeast4xl,
|
||||
isContentPanelAtLeast5xl,
|
||||
isContentPanelAtLeast6xl,
|
||||
isContentPanelAtLeast7xl,
|
||||
|
||||
isSubPanelAtLeast2xs,
|
||||
isSubPanelAtLeastXs,
|
||||
isSubPanelAtLeastSm,
|
||||
isSubPanelAtLeastMd,
|
||||
isSubPanelAtLeastLg,
|
||||
isSubPanelAtLeastXl,
|
||||
isSubPanelAtLeast2xl,
|
||||
isSubPanelAtLeast3xl,
|
||||
isSubPanelAtLeast4xl,
|
||||
isSubPanelAtLeast5xl,
|
||||
isSubPanelAtLeast6xl,
|
||||
isSubPanelAtLeast7xl,
|
||||
};
|
||||
|
||||
export const useContainerQueries = (): void => {
|
||||
const setScreenWidth = useAtomSetter(screenWidth);
|
||||
const setContentPanelWidth = useAtomSetter(contentPanelWidth);
|
||||
const setSubPanelWidth = useAtomSetter(subPanelWidth);
|
||||
|
||||
useOnResize(Ids.Body, (width) => setScreenWidth(width));
|
||||
useOnResize(Ids.ContentPanel, (width) => setContentPanelWidth(width));
|
||||
useOnResize(Ids.SubPanel, (width) => setSubPanelWidth(width));
|
||||
};
|
|
@ -0,0 +1,59 @@
|
|||
import { atom } from "jotai";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
import { useFetch } from "usehooks-ts";
|
||||
import { atomPairing, useAtomSetter } from "helpers/atoms";
|
||||
import {
|
||||
Languages,
|
||||
Currencies,
|
||||
Langui,
|
||||
processLangui,
|
||||
processCurrencies,
|
||||
processLanguages,
|
||||
} from "helpers/localData";
|
||||
import {
|
||||
LocalDataGetWebsiteInterfacesQuery,
|
||||
LocalDataGetCurrenciesQuery,
|
||||
LocalDataGetLanguagesQuery,
|
||||
} from "graphql/generated";
|
||||
import { LocalDataFile } from "graphql/fetchLocalData";
|
||||
|
||||
const languages = atomPairing(atom<Languages>([]));
|
||||
const currencies = atomPairing(atom<Currencies>([]));
|
||||
const langui = atomPairing(atom<Langui>({}));
|
||||
|
||||
export const localData = {
|
||||
languages: languages[0],
|
||||
currencies: currencies[0],
|
||||
langui: langui[0],
|
||||
};
|
||||
|
||||
const getFileName = (name: LocalDataFile): string => `/local-data/${name}.json`;
|
||||
|
||||
export const useLocalData = (): void => {
|
||||
const setLanguages = useAtomSetter(languages);
|
||||
const setCurrencies = useAtomSetter(currencies);
|
||||
const setLangui = useAtomSetter(langui);
|
||||
|
||||
const { locale } = useRouter();
|
||||
const { data: rawLanguages } = useFetch<LocalDataGetLanguagesQuery>(getFileName("languages"));
|
||||
const { data: rawCurrencies } = useFetch<LocalDataGetCurrenciesQuery>(getFileName("currencies"));
|
||||
const { data: rawLangui } = useFetch<LocalDataGetWebsiteInterfacesQuery>(
|
||||
getFileName("websiteInterfaces")
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[useLocalData] Refresh languages");
|
||||
setLanguages(processLanguages(rawLanguages));
|
||||
}, [rawLanguages, setLanguages]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[useLocalData] Refresh currencies");
|
||||
setCurrencies(processCurrencies(rawCurrencies));
|
||||
}, [rawCurrencies, setCurrencies]);
|
||||
|
||||
useEffect(() => {
|
||||
console.log("[useLocalData] Refresh langui");
|
||||
setLangui(processLangui(rawLangui, locale));
|
||||
}, [locale, rawLangui, setLangui]);
|
||||
};
|
|
@ -0,0 +1,103 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useLayoutEffect, useEffect } from "react";
|
||||
import { atom } from "jotai";
|
||||
import { atomWithStorage } from "jotai/utils";
|
||||
import { atomPairing, useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||
import { getDefaultPreferredLanguages } from "helpers/locales";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { usePrefersDarkMode } from "hooks/useMediaQuery";
|
||||
|
||||
export enum ThemeMode {
|
||||
Dark = "dark",
|
||||
Auto = "auto",
|
||||
Light = "light",
|
||||
}
|
||||
|
||||
const preferredLanguagesAtom = atomPairing(atomWithStorage<string[]>("preferredLanguages", []));
|
||||
const themeModeAtom = atomPairing(atomWithStorage<ThemeMode>("themeMode", ThemeMode.Auto));
|
||||
const darkModeAtom = atomPairing(atom(false));
|
||||
const fontSizeAtom = atomPairing(atomWithStorage("fontSize", 1));
|
||||
const dyslexicAtom = atomPairing(atomWithStorage("isDyslexic", false));
|
||||
const currencyAtom = atomPairing(atomWithStorage("currency", "USD"));
|
||||
const playerNameAtom = atomPairing(atomWithStorage("playerName", ""));
|
||||
|
||||
export const settings = {
|
||||
preferredLanguages: preferredLanguagesAtom,
|
||||
themeMode: themeModeAtom,
|
||||
darkMode: darkModeAtom,
|
||||
fontSize: fontSizeAtom,
|
||||
dyslexic: dyslexicAtom,
|
||||
currency: currencyAtom,
|
||||
playerName: playerNameAtom,
|
||||
};
|
||||
|
||||
export const useSettings = (): void => {
|
||||
const router = useRouter();
|
||||
const [preferredLanguages, setPreferredLanguages] = useAtomPair(preferredLanguagesAtom);
|
||||
const fontSize = useAtomGetter(fontSizeAtom);
|
||||
const isDyslexic = useAtomGetter(dyslexicAtom);
|
||||
const [isDarkMode, setDarkMode] = useAtomPair(darkModeAtom);
|
||||
const themeMode = useAtomGetter(themeModeAtom);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const html = document.getElementsByTagName("html")[0];
|
||||
if (isDefined(html)) {
|
||||
html.style.fontSize = `${fontSize * 100}%`;
|
||||
}
|
||||
}, [fontSize]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (isDyslexic) {
|
||||
next.classList.add("set-theme-font-dyslexic");
|
||||
next.classList.remove("set-theme-font-standard");
|
||||
} else {
|
||||
next.classList.add("set-theme-font-standard");
|
||||
next.classList.remove("set-theme-font-dyslexic");
|
||||
}
|
||||
}
|
||||
}, [isDyslexic]);
|
||||
|
||||
/* DARK MODE */
|
||||
const prefersDarkMode = usePrefersDarkMode();
|
||||
|
||||
useEffect(() => {
|
||||
setDarkMode(themeMode === ThemeMode.Auto ? prefersDarkMode : themeMode === ThemeMode.Dark);
|
||||
}, [prefersDarkMode, setDarkMode, themeMode]);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const next = document.getElementById("__next");
|
||||
if (isDefined(next)) {
|
||||
if (isDarkMode) {
|
||||
next.classList.add("set-theme-dark");
|
||||
next.classList.remove("set-theme-light");
|
||||
} else {
|
||||
next.classList.add("set-theme-light");
|
||||
next.classList.remove("set-theme-dark");
|
||||
}
|
||||
}
|
||||
}, [isDarkMode]);
|
||||
|
||||
/* PREFERRED LANGUAGES */
|
||||
|
||||
useEffect(() => {
|
||||
if (preferredLanguages.length === 0) {
|
||||
if (isDefinedAndNotEmpty(router.locale) && router.locales) {
|
||||
setPreferredLanguages(getDefaultPreferredLanguages(router.locale, router.locales));
|
||||
}
|
||||
} else if (router.locale !== preferredLanguages[0]) {
|
||||
/*
|
||||
* Using a timeout to the code getting stuck into a loop when reaching the website with a
|
||||
* different preferredLanguages[0] from router.locale
|
||||
*/
|
||||
setTimeout(
|
||||
async () =>
|
||||
router.replace(router.asPath, router.asPath, {
|
||||
locale: preferredLanguages[0],
|
||||
}),
|
||||
250
|
||||
);
|
||||
}
|
||||
}, [preferredLanguages, router, setPreferredLanguages]);
|
||||
};
|
|
@ -2,6 +2,7 @@ export const sendAnalytics = (category: string, event: string): void => {
|
|||
try {
|
||||
umami(`[${category}] ${event}`);
|
||||
} catch (error) {
|
||||
if (error instanceof ReferenceError) return;
|
||||
console.log(error);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { atom, PrimitiveAtom, Atom, WritableAtom, useAtom } from "jotai";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
type AtomPair<T> = [Atom<T>, WritableAtom<null, T>];
|
||||
|
||||
export const atomPairing = <T>(anAtom: PrimitiveAtom<T>): AtomPair<T> => {
|
||||
const getter = atom((get) => get(anAtom));
|
||||
const setter = atom(null, (_get, set, newText: T) => set(anAtom, newText));
|
||||
return [getter, setter];
|
||||
};
|
||||
|
||||
export const useAtomSetter = <T>(atomPair: AtomPair<T>): Dispatch<SetStateAction<T>> => {
|
||||
const [, setter] = useAtom(atomPair[1]);
|
||||
return setter as Dispatch<SetStateAction<T>>;
|
||||
};
|
||||
|
||||
export const useAtomGetter = <T>(atomPair: Atom<T> | AtomPair<T>): T => {
|
||||
const atomGet = Array.isArray(atomPair) ? atomPair[0] : atomPair;
|
||||
const [getter] = useAtom(atomGet);
|
||||
return getter;
|
||||
};
|
||||
|
||||
export const useAtomPair = <T>(atomPair: AtomPair<T>): [T, Dispatch<SetStateAction<T>>] => [
|
||||
useAtomGetter(atomPair),
|
||||
useAtomSetter(atomPair),
|
||||
];
|
|
@ -1,18 +0,0 @@
|
|||
import { Dispatch, SetStateAction, useEffect } from "react";
|
||||
import { useLocalStorage } from "usehooks-ts";
|
||||
import { usePrefersDarkMode } from "./useMediaQuery";
|
||||
|
||||
export const useDarkMode = (
|
||||
key: string,
|
||||
initialValue: boolean
|
||||
): [boolean, boolean, Dispatch<SetStateAction<boolean>>, Dispatch<SetStateAction<boolean>>] => {
|
||||
const [darkMode, setDarkMode] = useLocalStorage(key, initialValue);
|
||||
const prefersDarkMode = usePrefersDarkMode();
|
||||
const [selectedThemeMode, setSelectedThemeMode] = useLocalStorage("selectedThemeMode", false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedThemeMode) setDarkMode(prefersDarkMode);
|
||||
}, [selectedThemeMode, prefersDarkMode, setDarkMode]);
|
||||
|
||||
return [darkMode, selectedThemeMode, setDarkMode, setSelectedThemeMode];
|
||||
};
|
|
@ -1,6 +0,0 @@
|
|||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
|
||||
export const useIsTerminalMode = (): boolean => {
|
||||
const { playerName } = useUserSettings();
|
||||
return playerName === "root";
|
||||
};
|
|
@ -3,8 +3,8 @@ import { useEffect, useMemo, useState } from "react";
|
|||
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||
import { filterDefined, isDefined } from "helpers/others";
|
||||
import { getPreferredLanguage } from "helpers/locales";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
interface Props<T> {
|
||||
items: T[];
|
||||
|
@ -17,8 +17,8 @@ export const useSmartLanguage = <T>({
|
|||
languageExtractor,
|
||||
transform = (item) => item,
|
||||
}: Props<T>): [T | undefined, typeof LanguageSwitcher, Parameters<typeof LanguageSwitcher>[0]] => {
|
||||
const { preferredLanguages } = useUserSettings();
|
||||
const { languages } = useLocalData();
|
||||
const preferredLanguages = useAtomGetter(atoms.settings.preferredLanguages);
|
||||
const languages = useAtomGetter(atoms.localData.languages);
|
||||
const router = useRouter();
|
||||
|
||||
const availableLocales = useMemo(() => {
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
import { Dispatch, SetStateAction, useEffect, useState } from "react";
|
||||
import { isDefined } from "helpers/others";
|
||||
|
||||
export const useStateWithLocalStorage = <T>(
|
||||
key: string,
|
||||
initialValue: T
|
||||
): [T, Dispatch<SetStateAction<T>>] => {
|
||||
const [value, setValue] = useState<T>(initialValue);
|
||||
const [isFromLocaleStorage, setFromLocaleStorage] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
const item = localStorage.getItem(key);
|
||||
if (isDefined(item)) {
|
||||
setValue(JSON.parse(item) as T);
|
||||
} else {
|
||||
setValue(initialValue);
|
||||
}
|
||||
setFromLocaleStorage(true);
|
||||
} catch (error) {
|
||||
console.warn(`Error reading localStorage key “${key}”:`, error);
|
||||
setValue(initialValue);
|
||||
}
|
||||
}, [initialValue, key]);
|
||||
|
||||
useEffect(() => {
|
||||
if (isFromLocaleStorage) localStorage.setItem(key, JSON.stringify(value));
|
||||
}, [value, key, isFromLocaleStorage]);
|
||||
|
||||
return [value, setValue];
|
||||
};
|
|
@ -5,7 +5,8 @@ import { ContentPanel } from "components/Containers/ContentPanel";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Img } from "components/Img";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -15,7 +16,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
interface Props extends AppLayoutRequired {}
|
||||
|
||||
const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<AppLayout
|
||||
contentPanel={
|
||||
|
|
|
@ -5,7 +5,8 @@ import { ContentPanel } from "components/Containers/ContentPanel";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Img } from "components/Img";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -15,7 +16,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
interface Props extends AppLayoutRequired {}
|
||||
|
||||
const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<AppLayout
|
||||
contentPanel={
|
||||
|
|
|
@ -9,7 +9,6 @@ import "@fontsource/zen-maru-gothic/900.css";
|
|||
|
||||
import type { AppProps } from "next/app";
|
||||
import Script from "next/script";
|
||||
import { AppContextProvider } from "contexts/AppLayoutContext";
|
||||
|
||||
import "styles/debug.css";
|
||||
import "styles/formatted.css";
|
||||
|
@ -17,31 +16,31 @@ import "styles/others.css";
|
|||
import "styles/rc-slider.css";
|
||||
import "styles/tippy.css";
|
||||
|
||||
import { TerminalContextProvider } from "contexts/TerminalContext";
|
||||
import { UserSettingsProvider as UserSettingsContextProvider } from "contexts/UserSettingsContext";
|
||||
import { LocalDataContextProvider } from "contexts/LocalDataContext";
|
||||
import { ContainerQueriesContextProvider } from "contexts/ContainerQueriesContext";
|
||||
import { LightBoxContextProvider } from "contexts/LightBoxContext";
|
||||
import { useLocalData } from "contexts/localData";
|
||||
import { useAppLayout } from "contexts/appLayout";
|
||||
import { LightBoxProvider } from "contexts/LightBoxProvider";
|
||||
import { SettingsPopup } from "components/Panels/SettingsPopup";
|
||||
import { useSettings } from "contexts/settings";
|
||||
import { useContainerQueries } from "contexts/containerQueries";
|
||||
|
||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
||||
<LocalDataContextProvider>
|
||||
<AppContextProvider>
|
||||
<UserSettingsContextProvider>
|
||||
<ContainerQueriesContextProvider>
|
||||
<TerminalContextProvider>
|
||||
<LightBoxContextProvider>
|
||||
<Script
|
||||
async
|
||||
defer
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_ID}
|
||||
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
||||
/>
|
||||
<props.Component {...props.pageProps} />
|
||||
</LightBoxContextProvider>
|
||||
</TerminalContextProvider>
|
||||
</ContainerQueriesContextProvider>
|
||||
</UserSettingsContextProvider>
|
||||
</AppContextProvider>
|
||||
</LocalDataContextProvider>
|
||||
);
|
||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => {
|
||||
useLocalData();
|
||||
useAppLayout();
|
||||
useSettings();
|
||||
useContainerQueries();
|
||||
|
||||
return (
|
||||
<>
|
||||
<SettingsPopup />
|
||||
<LightBoxProvider />
|
||||
<Script
|
||||
async
|
||||
defer
|
||||
data-website-id={process.env.NEXT_PUBLIC_UMAMI_ID}
|
||||
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
||||
/>
|
||||
<props.Component {...props.pageProps} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
export default AccordsLibraryApp;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PostPage } from "components/PostPage";
|
||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -8,7 +9,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
*/
|
||||
|
||||
const AccordsHandbook = (props: PostStaticProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<PostPage
|
||||
{...props}
|
||||
|
|
|
@ -7,8 +7,8 @@ import { cIf, cJoin } from "helpers/className";
|
|||
import { randomInt } from "helpers/numbers";
|
||||
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -17,8 +17,8 @@ import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|||
|
||||
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||
const router = useRouter();
|
||||
const { langui } = useLocalData();
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
const [formResponse, setFormResponse] = useState("");
|
||||
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
||||
|
||||
|
|
|
@ -7,7 +7,8 @@ import { SubPanel } from "components/Containers/SubPanel";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -17,7 +18,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
interface Props extends AppLayoutRequired {}
|
||||
|
||||
const AboutUs = (props: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<AppLayout
|
||||
subPanel={
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PostPage } from "components/PostPage";
|
||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -8,7 +9,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
*/
|
||||
|
||||
const Legality = (props: PostStaticProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<PostPage
|
||||
{...props}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { PostPage } from "components/PostPage";
|
||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -8,7 +9,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
*/
|
||||
|
||||
const SharingPolicy = (props: PostStaticProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<PostPage
|
||||
{...props}
|
||||
|
|
|
@ -8,7 +8,8 @@ import { Icon } from "components/Ico";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -18,7 +19,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
interface Props extends AppLayoutRequired {}
|
||||
|
||||
const Archives = (props: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
|
|
|
@ -22,8 +22,8 @@ import { SmartList } from "components/SmartList";
|
|||
import { cIf } from "helpers/className";
|
||||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -45,9 +45,9 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
||||
|
|
|
@ -22,8 +22,8 @@ import { compareDate } from "helpers/date";
|
|||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -44,9 +44,9 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||
|
||||
|
|
|
@ -10,7 +10,6 @@ import { NavOption } from "components/PanelComponents/NavOption";
|
|||
import { ReturnButton } from "components/PanelComponents/ReturnButton";
|
||||
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
||||
import { SubPanel } from "components/Containers/SubPanel";
|
||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
||||
import { GetVideoQuery } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
||||
|
@ -18,8 +17,8 @@ import { filterHasAttributes, isDefined } from "helpers/others";
|
|||
import { getVideoFile } from "helpers/videos";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -31,9 +30,8 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const { setSubPanelOpen } = useAppLayout();
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const router = useRouter();
|
||||
|
||||
const subPanel = useMemo(
|
||||
|
@ -47,29 +45,12 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
|||
|
||||
<HorizontalLine />
|
||||
|
||||
<NavOption
|
||||
title={langui.video}
|
||||
url="#video"
|
||||
border
|
||||
onClick={() => setSubPanelOpen(false)}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
title={langui.channel}
|
||||
url="#channel"
|
||||
border
|
||||
onClick={() => setSubPanelOpen(false)}
|
||||
/>
|
||||
|
||||
<NavOption
|
||||
title={langui.description}
|
||||
url="#description"
|
||||
border
|
||||
onClick={() => setSubPanelOpen(false)}
|
||||
/>
|
||||
<NavOption title={langui.video} url="#video" border />
|
||||
<NavOption title={langui.channel} url="#channel" border />
|
||||
<NavOption title={langui.description} url="#description" border />
|
||||
</SubPanel>
|
||||
),
|
||||
[setSubPanelOpen, langui]
|
||||
[langui]
|
||||
);
|
||||
|
||||
const contentPanel = useMemo(
|
||||
|
|
|
@ -19,7 +19,8 @@ import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/local
|
|||
import { getDescription } from "helpers/description";
|
||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -32,7 +33,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: chronicle.translations,
|
||||
languageExtractor: useCallback(
|
||||
|
|
|
@ -12,7 +12,8 @@ import { getOpenGraph } from "helpers/openGraph";
|
|||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -24,7 +25,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
<SubPanel>
|
||||
|
|
|
@ -32,8 +32,8 @@ import { TranslatedPreviewLine } from "components/PreviewLine";
|
|||
import { cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Ids } from "types/ids";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -45,8 +45,11 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||
const { isContentPanelAtLeast2xl, is1ColumnLayout } = useContainerQueries();
|
||||
const { langui, languages } = useLocalData();
|
||||
const isContentPanelAtLeast2xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast2xl);
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const languages = useAtomGetter(atoms.localData.languages);
|
||||
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: content.translations,
|
||||
|
|
|
@ -25,8 +25,8 @@ import { TranslatedPreviewCard } from "components/PreviewCard";
|
|||
import { cJoin, cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -50,8 +50,8 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const Contents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui } = useLocalData();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||
DEFAULT_FILTERS_STATE.groupingMethod
|
||||
|
|
|
@ -18,8 +18,8 @@ import { TranslatedPreviewCard } from "components/PreviewCard";
|
|||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
|
||||
|
||||
/*
|
||||
|
@ -34,8 +34,8 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
|
@ -273,7 +273,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
|
|||
*/
|
||||
|
||||
const NoContentNorFolderMessage = () => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<div className="grid place-content-center">
|
||||
<div
|
||||
|
|
|
@ -936,13 +936,8 @@ const LightThemeSection = ({ className, children }: ThemedSectionProps) => (
|
|||
);
|
||||
|
||||
const WhiteSection = ({ className, children }: ThemedSectionProps) => (
|
||||
<div
|
||||
className={cJoin(
|
||||
`mb-12 rounded-xl bg-[white] py-10 px-14 text-black drop-shadow-lg shadow-shade
|
||||
set-theme-light`,
|
||||
className
|
||||
)}>
|
||||
{children}
|
||||
<div className="mb-12 rounded-xl bg-[white] py-10 px-14 drop-shadow-lg shadow-shade">
|
||||
<div className={cJoin("text-black set-theme-light", className)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@ import { PostPage } from "components/PostPage";
|
|||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -11,8 +11,8 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
*/
|
||||
|
||||
const Home = ({ ...otherProps }: PostStaticProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||
|
||||
if (isTerminalMode) {
|
||||
return (
|
||||
|
|
|
@ -51,10 +51,8 @@ import { useIntersectionList } from "hooks/useIntersectionList";
|
|||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Ids } from "types/ids";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -74,14 +72,18 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||
const { currency } = useUserSettings();
|
||||
const { langui, currencies } = useLocalData();
|
||||
const { isContentPanelAtLeast3xl, isContentPanelAtLeastSm } = useContainerQueries();
|
||||
const currency = useAtomGetter(atoms.settings.currency);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||
|
||||
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
||||
const isContentPanelAtLeastSm = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastSm);
|
||||
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const router = useRouter();
|
||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(false);
|
||||
|
||||
const { showLightBox } = useLightBox();
|
||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||
|
||||
useScrollTopOnChange(Ids.ContentPanel, [item]);
|
||||
const currentIntersection = useIntersectionList(intersectionIds);
|
||||
|
@ -692,7 +694,7 @@ const ContentLine = ({
|
|||
parentSlug,
|
||||
condensed,
|
||||
}: ContentLineProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
|
||||
const [selectedTranslation] = useSmartLanguage({
|
||||
items: content?.translations ?? [],
|
||||
|
|
|
@ -41,14 +41,13 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
|||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { prettyInlineTitle, prettySlug } from "helpers/formatters";
|
||||
import { useFullscreen } from "hooks/useFullscreen";
|
||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
const CUSTOM_DARK_DROPSHADOW = `
|
||||
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
|
||||
drop-shadow(0 1em 1em rgb(var(--theme-color-shade) / 40%))
|
||||
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
|
||||
drop-shadow(0 1em 1em rgb(var(--theme-color-shade) / 40%))
|
||||
drop-shadow(0 2em 2em rgb(var(--theme-color-shade) / 60%))
|
||||
drop-shadow(0 12em 12em rgb(var(--theme-color-shade) / 80%))`;
|
||||
|
||||
|
@ -90,9 +89,9 @@ const LibrarySlug = ({
|
|||
item,
|
||||
...otherProps
|
||||
}: Props): JSX.Element => {
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const { darkMode } = useUserSettings();
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
||||
const {
|
||||
filterSettings,
|
||||
isSidePagesEnabled,
|
||||
|
@ -453,7 +452,7 @@ const LibrarySlug = ({
|
|||
display: "grid",
|
||||
placeContent: "center",
|
||||
filter: filterSettings.dropShadow
|
||||
? darkMode
|
||||
? isDarkMode
|
||||
? CUSTOM_DARK_DROPSHADOW
|
||||
: CUSTOM_LIGHT_DROPSHADOW
|
||||
: undefined,
|
||||
|
@ -631,7 +630,7 @@ const LibrarySlug = ({
|
|||
is1ColumnLayout,
|
||||
currentZoom,
|
||||
filterSettings,
|
||||
darkMode,
|
||||
isDarkMode,
|
||||
pageHeight,
|
||||
effectiveDisplayMode,
|
||||
firstPage,
|
||||
|
@ -798,7 +797,7 @@ interface PageFiltersProps {
|
|||
}
|
||||
|
||||
const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
||||
const { darkMode } = useUserSettings();
|
||||
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
||||
const commonCss = useMemo(
|
||||
() => cJoin("absolute inset-0", cIf(page === "right", "[background-position-x:-100%]")),
|
||||
[page]
|
||||
|
@ -823,7 +822,7 @@ const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
|||
commonCss,
|
||||
`bg-blend-lighten mix-blend-multiply [background-image:url(/reader/book-fold.webp)]
|
||||
[background-size:200%_100%]`,
|
||||
cIf(!darkMode, "bg-[#FFF]/50")
|
||||
cIf(!isDarkMode, "bg-[#FFF]/50")
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
|
@ -834,7 +833,7 @@ const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
|||
className={cJoin(
|
||||
commonCss,
|
||||
`bg-blend-lighten mix-blend-multiply [background-size:200%_100%]`,
|
||||
cIf(!darkMode, "bg-[#FFF]/50"),
|
||||
cIf(!isDarkMode, "bg-[#FFF]/50"),
|
||||
cIf(
|
||||
page === "single",
|
||||
"[background-image:url(/reader/lighting-single-page.webp)]",
|
||||
|
@ -846,7 +845,7 @@ const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
|||
className={cJoin(
|
||||
commonCss,
|
||||
`bg-blend-lighten mix-blend-soft-light [background-size:200%_100%]`,
|
||||
cIf(!darkMode, "bg-[#FFF]/30"),
|
||||
cIf(!isDarkMode, "bg-[#FFF]/30"),
|
||||
cIf(
|
||||
page === "single",
|
||||
"[background-image:url(/reader/specular-single-page.webp)]",
|
||||
|
@ -889,8 +888,8 @@ interface ScanSetProps {
|
|||
}
|
||||
|
||||
const ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps): JSX.Element => {
|
||||
const { is1ColumnLayout } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: scanSet,
|
||||
languageExtractor: useCallback(
|
||||
|
|
|
@ -31,9 +31,9 @@ import { HorizontalLine } from "components/HorizontalLine";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -62,9 +62,11 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const Library = ({ items, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui, currencies } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||
|
||||
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
||||
|
|
|
@ -5,7 +5,8 @@ import { SubPanel } from "components/Containers/SubPanel";
|
|||
import { Icon } from "components/Ico";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -14,7 +15,7 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
|
||||
interface Props extends AppLayoutRequired {}
|
||||
const Merch = (props: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
return (
|
||||
<AppLayout
|
||||
subPanel={
|
||||
|
|
|
@ -4,14 +4,13 @@ import { PostPage } from "components/PostPage";
|
|||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { PostWithTranslations } from "types/types";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { prettyTerminalBoxedTitle } from "helpers/terminal";
|
||||
import { prettyMarkdown } from "helpers/description";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
/*
|
||||
* ╭────────╮
|
||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||
|
@ -20,8 +19,8 @@ import { useLocalData } from "contexts/LocalDataContext";
|
|||
interface Props extends PostStaticProps {}
|
||||
|
||||
const LibrarySlug = (props: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||
const router = useRouter();
|
||||
|
||||
if (isTerminalMode) {
|
||||
|
|
|
@ -23,10 +23,9 @@ import { HorizontalLine } from "components/HorizontalLine";
|
|||
import { cIf } from "helpers/className";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -48,8 +47,8 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const { langui } = useLocalData();
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
const {
|
||||
|
@ -57,7 +56,7 @@ const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
|||
toggle: toggleKeepInfoVisible,
|
||||
setValue: setKeepInfoVisible,
|
||||
} = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
|
|
|
@ -22,10 +22,8 @@ import { cIf, cJoin } from "helpers/className";
|
|||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { useLightBox } from "contexts/LightBoxContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -37,10 +35,10 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const router = useRouter();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const { showLightBox } = useLightBox();
|
||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: page.translations,
|
||||
languageExtractor: useCallback(
|
||||
|
@ -49,7 +47,7 @@ const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
|||
[]
|
||||
),
|
||||
});
|
||||
const { is3ColumnsLayout } = useContainerQueries();
|
||||
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||
|
||||
const subPanel = useMemo(
|
||||
() => (
|
||||
|
|
|
@ -31,7 +31,8 @@ import { TranslatedNavOption } from "components/PanelComponents/NavOption";
|
|||
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭────────╮
|
||||
|
@ -44,7 +45,7 @@ interface Props extends AppLayoutRequired {
|
|||
}
|
||||
|
||||
const Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const ids = useMemo(
|
||||
() =>
|
||||
filterHasAttributes(chronologyEras, ["attributes"] as const).map(
|
||||
|
@ -315,7 +316,7 @@ interface ChronologyEventProps {
|
|||
}
|
||||
|
||||
export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Element => {
|
||||
const { langui } = useLocalData();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||
items: event.translations ?? [],
|
||||
languageExtractor: useCallback(
|
||||
|
|
|
@ -26,9 +26,8 @@ import { cIf } from "helpers/className";
|
|||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
||||
import { useLocalData } from "contexts/LocalDataContext";
|
||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -52,9 +51,9 @@ interface Props extends AppLayoutRequired {
|
|||
|
||||
const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
|
||||
const hoverable = useDeviceSupportsHover();
|
||||
const { langui } = useLocalData();
|
||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
||||
const isTerminalMode = useIsTerminalMode();
|
||||
const langui = useAtomGetter(atoms.localData.langui);
|
||||
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||
|
||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||
|
||||
|
|
|
@ -45,12 +45,6 @@ export interface ChronicleWithTranslations extends Omit<Chronicle, "translations
|
|||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export type RequiredNonNullable<T> = {
|
||||
[P in keyof T]-?: NonNullable<T[P]>;
|
||||
};
|
||||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
export enum LibraryItemUserStatus {
|
||||
None = 0,
|
||||
Want = 1,
|
||||
|
|
Loading…
Reference in New Issue