Merge branch 'main' into next13
This commit is contained in:
commit
2aea7fa040
|
@ -39,7 +39,8 @@
|
||||||
- Support for Container Queries (media queries at the element level)
|
- 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.
|
- 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)
|
- 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
|
- Persistent app state using LocalStorage and SessionStorage
|
||||||
- Accessibility
|
- Accessibility
|
||||||
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
|
- Gestures using [react-swipeable](https://www.npmjs.com/package/react-swipeable)
|
||||||
|
@ -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
|
- 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
|
- Check for completeness, conformity, and integrity
|
||||||
- Code quality and style
|
- Code quality and style
|
||||||
|
|
||||||
- React Strict Mode
|
- 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)
|
- [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)
|
- [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",
|
"@tippyjs/react": "^4.2.6",
|
||||||
"autoprefixer": "^10.4.13",
|
"autoprefixer": "^10.4.13",
|
||||||
"graphql-request": "^5.0.0",
|
"graphql-request": "^5.0.0",
|
||||||
|
"jotai": "^1.9.0",
|
||||||
"markdown-to-jsx": "^7.1.7",
|
"markdown-to-jsx": "^7.1.7",
|
||||||
"next": "^13.0.1",
|
"next": "^13.0.1",
|
||||||
"nodemailer": "^6.8.0",
|
"nodemailer": "^6.8.0",
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Ico, Icon } from "./Ico";
|
import { Ico, Icon } from "./Ico";
|
||||||
import { ToolTip } from "./ToolTip";
|
import { ToolTip } from "./ToolTip";
|
||||||
import { cJoin } from "helpers/className";
|
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 => {
|
export const AnchorShare = ({ id, className }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<ToolTip content={langui.copy_anchor_link} trigger="mouseenter" className="text-sm">
|
<ToolTip content={langui.copy_anchor_link} trigger="mouseenter" className="text-sm">
|
||||||
<ToolTip content={langui.anchor_link_copied} trigger="click" 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 { SafariPopup } from "./Panels/SafariPopup";
|
||||||
import { isDefined, isUndefined } from "helpers/others";
|
import { isDefined, isUndefined } from "helpers/others";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -45,39 +44,34 @@ export const AppLayout = ({
|
||||||
subPanelIcon = Icon.Tune,
|
subPanelIcon = Icon.Tune,
|
||||||
contentPanelScroolbar = true,
|
contentPanelScroolbar = true,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const {
|
const isMainPanelReduced = useAtomGetter(atoms.layout.mainPanelReduced);
|
||||||
mainPanelOpen,
|
const [isSubPanelOpened, setSubPanelOpened] = useAtomPair(atoms.layout.subPanelOpened);
|
||||||
mainPanelReduced,
|
const [isMainPanelOpened, setMainPanelOpened] = useAtomPair(atoms.layout.mainPanelOpened);
|
||||||
menuGestures,
|
const isMenuGesturesEnabled = useAtomGetter(atoms.layout.menuGesturesEnabled);
|
||||||
subPanelOpen,
|
|
||||||
setMainPanelOpen,
|
|
||||||
setSubPanelOpen,
|
|
||||||
toggleMainPanelOpen,
|
|
||||||
toggleSubPanelOpen,
|
|
||||||
} = useAppLayout();
|
|
||||||
|
|
||||||
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({
|
const handlers = useSwipeable({
|
||||||
onSwipedLeft: (SwipeEventData) => {
|
onSwipedLeft: (SwipeEventData) => {
|
||||||
if (menuGestures) {
|
if (isMenuGesturesEnabled) {
|
||||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||||
if (mainPanelOpen) {
|
if (isMainPanelOpened) {
|
||||||
setMainPanelOpen(false);
|
setMainPanelOpened(false);
|
||||||
} else if (isDefined(subPanel) && isDefined(contentPanel)) {
|
} else if (isDefined(subPanel) && isDefined(contentPanel)) {
|
||||||
setSubPanelOpen(true);
|
setSubPanelOpened(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onSwipedRight: (SwipeEventData) => {
|
onSwipedRight: (SwipeEventData) => {
|
||||||
if (menuGestures) {
|
if (isMenuGesturesEnabled) {
|
||||||
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
if (SwipeEventData.velocity < SENSIBILITY_SWIPE) return;
|
||||||
if (subPanelOpen) {
|
if (isSubPanelOpened) {
|
||||||
setSubPanelOpen(false);
|
setSubPanelOpened(false);
|
||||||
} else {
|
} else {
|
||||||
setMainPanelOpen(true);
|
setMainPanelOpened(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -99,7 +93,7 @@ export const AppLayout = ({
|
||||||
style={{
|
style={{
|
||||||
gridTemplateColumns: is1ColumnLayout
|
gridTemplateColumns: is1ColumnLayout
|
||||||
? "1fr"
|
? "1fr"
|
||||||
: `${mainPanelReduced ? layout.mainMenuReduced : layout.mainMenu}rem ${
|
: `${isMainPanelReduced ? layout.mainMenuReduced : layout.mainMenu}rem ${
|
||||||
isDefined(subPanel) ? layout.subMenu : 0
|
isDefined(subPanel) ? layout.subMenu : 0
|
||||||
}rem 1fr`,
|
}rem 1fr`,
|
||||||
}}>
|
}}>
|
||||||
|
@ -128,7 +122,7 @@ export const AppLayout = ({
|
||||||
`absolute inset-0 transition-filter duration-500
|
`absolute inset-0 transition-filter duration-500
|
||||||
[grid-area:content]`,
|
[grid-area:content]`,
|
||||||
cIf(
|
cIf(
|
||||||
(mainPanelOpen || subPanelOpen) && is1ColumnLayout,
|
(isMainPanelOpened || isSubPanelOpened) && is1ColumnLayout,
|
||||||
"z-10 backdrop-blur",
|
"z-10 backdrop-blur",
|
||||||
"pointer-events-none touch-none"
|
"pointer-events-none touch-none"
|
||||||
)
|
)
|
||||||
|
@ -136,11 +130,15 @@ export const AppLayout = ({
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"absolute inset-0 bg-shade transition-opacity duration-500",
|
"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={() => {
|
onClick={() => {
|
||||||
setMainPanelOpen(false);
|
setMainPanelOpened(false);
|
||||||
setSubPanelOpen(false);
|
setSubPanelOpened(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -175,7 +173,7 @@ export const AppLayout = ({
|
||||||
"[grid-area:sub]"
|
"[grid-area:sub]"
|
||||||
),
|
),
|
||||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)] border-l"),
|
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")
|
cIf(is1ColumnLayout && turnSubIntoContent, "w-full border-l-0")
|
||||||
)}>
|
)}>
|
||||||
{subPanel}
|
{subPanel}
|
||||||
|
@ -189,7 +187,7 @@ export const AppLayout = ({
|
||||||
transition-transform duration-300 scrollbar-none texture-paper-dots`,
|
transition-transform duration-300 scrollbar-none texture-paper-dots`,
|
||||||
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
|
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
|
||||||
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
|
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
|
||||||
cIf(!mainPanelOpen && is1ColumnLayout, "-translate-x-full")
|
cIf(!isMainPanelOpened && is1ColumnLayout, "-translate-x-full")
|
||||||
)}>
|
)}>
|
||||||
<MainPanel />
|
<MainPanel />
|
||||||
</div>
|
</div>
|
||||||
|
@ -202,11 +200,11 @@ export const AppLayout = ({
|
||||||
cIf(!is1ColumnLayout, "hidden")
|
cIf(!is1ColumnLayout, "hidden")
|
||||||
)}>
|
)}>
|
||||||
<Ico
|
<Ico
|
||||||
icon={mainPanelOpen ? Icon.Close : Icon.Menu}
|
icon={isMainPanelOpened ? Icon.Close : Icon.Menu}
|
||||||
className="cursor-pointer !text-2xl"
|
className="cursor-pointer !text-2xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleMainPanelOpen();
|
setMainPanelOpened((current) => !current);
|
||||||
setSubPanelOpen(false);
|
setSubPanelOpened(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<p
|
<p
|
||||||
|
@ -220,11 +218,11 @@ export const AppLayout = ({
|
||||||
</p>
|
</p>
|
||||||
{isDefined(subPanel) && !turnSubIntoContent && (
|
{isDefined(subPanel) && !turnSubIntoContent && (
|
||||||
<Ico
|
<Ico
|
||||||
icon={subPanelOpen ? Icon.Close : subPanelIcon}
|
icon={isSubPanelOpened ? Icon.Close : subPanelIcon}
|
||||||
className="cursor-pointer !text-2xl"
|
className="cursor-pointer !text-2xl"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
toggleSubPanelOpen();
|
setSubPanelOpened((current) => !current);
|
||||||
setMainPanelOpen(false);
|
setMainPanelOpened(false);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { useTerminalContext } from "contexts/TerminalContext";
|
|
||||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
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,
|
content,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const [childrenPaths, setChildrenPaths] = useState(propsChildrenPaths);
|
const [childrenPaths, setChildrenPaths] = useState(propsChildrenPaths);
|
||||||
const { darkMode, setPlayerName } = useUserSettings();
|
const setPlayerName = useAtomSetter(atoms.settings.playerName);
|
||||||
const { previousCommands, previousLines, setPreviousCommands, setPreviousLines } =
|
|
||||||
useTerminalContext();
|
const [previousCommands, setPreviousCommands] = useAtomPair(atoms.terminal.previousCommands);
|
||||||
|
const [previousLines, setPreviousLines] = useAtomPair(atoms.terminal.previousLines);
|
||||||
|
|
||||||
const [line, setLine] = useState("");
|
const [line, setLine] = useState("");
|
||||||
const [displayCurrentLine, setDisplayCurrentLine] = useState(true);
|
const [displayCurrentLine, setDisplayCurrentLine] = useState(true);
|
||||||
const [previousCommandIndex, setPreviousCommandIndex] = useState(0);
|
const [previousCommandIndex, setPreviousCommandIndex] = useState(0);
|
||||||
|
@ -232,11 +234,7 @@ export const Terminal = ({
|
||||||
}, [line]);
|
}, [line]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className={cJoin("h-screen overflow-hidden bg-light set-theme-font-standard")}>
|
||||||
className={cJoin(
|
|
||||||
"h-screen overflow-hidden bg-light set-theme-font-standard",
|
|
||||||
cIf(darkMode, "set-theme-dark", "set-theme-light")
|
|
||||||
)}>
|
|
||||||
<div
|
<div
|
||||||
ref={terminalWindowRef}
|
ref={terminalWindowRef}
|
||||||
className="h-full overflow-scroll scroll-auto p-6 scrollbar-none">
|
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";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -25,7 +26,7 @@ export const ContentPanel = ({
|
||||||
children,
|
children,
|
||||||
className,
|
className,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { isContentPanelAtLeast3xl } = useContainerQueries();
|
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full">
|
<div className="grid h-full">
|
||||||
<main
|
<main
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
import { useHotkeys } from "react-hotkeys-hook";
|
import { useHotkeys } from "react-hotkeys-hook";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { cIf, cJoin } from "helpers/className";
|
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,
|
hideBackground = false,
|
||||||
padding = true,
|
padding = true,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { setMenuGestures } = useAppLayout();
|
const setMenuGesturesEnabled = useAtomSetter(atoms.layout.menuGesturesEnabled);
|
||||||
|
|
||||||
useHotkeys("escape", () => onCloseRequest?.(), {}, [onCloseRequest]);
|
useHotkeys("escape", () => onCloseRequest?.(), {}, [onCloseRequest]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMenuGestures(!isVisible);
|
setMenuGesturesEnabled(!isVisible);
|
||||||
}, [setMenuGestures, isVisible]);
|
}, [isVisible, setMenuGesturesEnabled]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<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";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -13,7 +14,7 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const SubPanel = ({ children }: Props): JSX.Element => {
|
export const SubPanel = ({ children }: Props): JSX.Element => {
|
||||||
const { isSubPanelAtLeastXs } = useContainerQueries();
|
const isSubPanelAtLeastXs = useAtomGetter(atoms.containerQueries.isSubPanelAtLeastXs);
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
|
|
|
@ -23,8 +23,8 @@ export const UpPressable = ({
|
||||||
href={href}
|
href={href}
|
||||||
onFocusChanged={setFocused}
|
onFocusChanged={setFocused}
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
`overflow-hidden rounded-md drop-shadow-lg transition-all duration-300 shadow-shade`,
|
`drop-shadow-lg transition-all duration-300 shadow-shade`,
|
||||||
cIf(!noBackground, "bg-highlight"),
|
cIf(!noBackground, "overflow-hidden rounded-md bg-highlight"),
|
||||||
cIf(
|
cIf(
|
||||||
disabled,
|
disabled,
|
||||||
"cursor-not-allowed opacity-50 grayscale",
|
"cursor-not-allowed opacity-50 grayscale",
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { cJoin } from "helpers/className";
|
||||||
import { prettyLanguage } from "helpers/formatters";
|
import { prettyLanguage } from "helpers/formatters";
|
||||||
import { iterateMap } from "helpers/others";
|
import { iterateMap } from "helpers/others";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
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,
|
onLanguageChanged,
|
||||||
showBadge = true,
|
showBadge = true,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { languages } = useLocalData();
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
return (
|
return (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={
|
content={
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import router from "next/router";
|
import router from "next/router";
|
||||||
import { PointerEventHandler, useState } from "react";
|
import { MouseEventHandler, useState } from "react";
|
||||||
import { isDefined } from "helpers/others";
|
import { isDefined } from "helpers/others";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
@ -8,7 +8,7 @@ interface Props {
|
||||||
allowNewTab?: boolean;
|
allowNewTab?: boolean;
|
||||||
alwaysNewTab?: boolean;
|
alwaysNewTab?: boolean;
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
onClick?: PointerEventHandler<HTMLDivElement>;
|
onClick?: MouseEventHandler<HTMLDivElement>;
|
||||||
onFocusChanged?: (isFocused: boolean) => void;
|
onFocusChanged?: (isFocused: boolean) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -28,19 +28,19 @@ export const Link = ({
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={className}
|
className={className}
|
||||||
onPointerLeave={() => {
|
onMouseLeave={() => {
|
||||||
setIsValidClick(false);
|
setIsValidClick(false);
|
||||||
onFocusChanged?.(false);
|
onFocusChanged?.(false);
|
||||||
}}
|
}}
|
||||||
onContextMenu={(event) => event.preventDefault()}
|
onContextMenu={(event) => event.preventDefault()}
|
||||||
onPointerDown={(event) => {
|
onMouseDown={(event) => {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
onFocusChanged?.(true);
|
onFocusChanged?.(true);
|
||||||
setIsValidClick(true);
|
setIsValidClick(true);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
onPointerUp={(event) => {
|
onMouseUp={(event) => {
|
||||||
onFocusChanged?.(false);
|
onFocusChanged?.(false);
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
if (isDefined(onClick)) {
|
if (isDefined(onClick)) {
|
||||||
|
|
|
@ -3,8 +3,9 @@ import { Button } from "components/Inputs/Button";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { LibraryItemUserStatus } from "types/types";
|
import { LibraryItemUserStatus } from "types/types";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
|
||||||
import { useLibraryItemUserStatus } from "hooks/useLibraryItemUserStatus";
|
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 => {
|
export const PreviewCardCTAs = ({ id, expand = false }: Props): JSX.Element => {
|
||||||
const { libraryItemUserStatus, setLibraryItemUserStatus } = useLibraryItemUserStatus();
|
const { libraryItemUserStatus, setLibraryItemUserStatus } = useLibraryItemUserStatus();
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -10,7 +10,8 @@ import { Ids } from "types/ids";
|
||||||
import { UploadImageFragment } from "graphql/generated";
|
import { UploadImageFragment } from "graphql/generated";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { isDefined } from "helpers/others";
|
import { isDefined } from "helpers/others";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
import { atoms } from "contexts/atoms";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onCloseRequest: () => void;
|
onCloseRequest: () => void;
|
||||||
|
@ -138,7 +139,7 @@ const ControlButtons = ({
|
||||||
onCloseRequest,
|
onCloseRequest,
|
||||||
toggleFullscreen,
|
toggleFullscreen,
|
||||||
}: ControlButtonsProps): JSX.Element => {
|
}: ControlButtonsProps): JSX.Element => {
|
||||||
const { is1ColumnLayout } = useContainerQueries();
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
|
|
||||||
const PreviousButton = () => (
|
const PreviousButton = () => (
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -13,10 +13,8 @@ import { AnchorShare } from "components/AnchorShare";
|
||||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||||
import { Ico, Icon } from "components/Ico";
|
import { Ico, Icon } from "components/Ico";
|
||||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|
||||||
import { useLightBox } from "contexts/LightBoxContext";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -31,10 +29,10 @@ interface MarkdawnProps {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Element => {
|
export const Markdawn = ({ className, text: rawText }: MarkdawnProps): JSX.Element => {
|
||||||
const { playerName } = useUserSettings();
|
const playerName = useAtomGetter(atoms.settings.playerName);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { isContentPanelAtLeastLg } = useContainerQueries();
|
const isContentPanelAtLeastLg = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastLg);
|
||||||
const { showLightBox } = useLightBox();
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
|
||||||
/* eslint-disable no-irregular-whitespace */
|
/* eslint-disable no-irregular-whitespace */
|
||||||
const text = useMemo(
|
const text = useMemo(
|
||||||
|
@ -232,7 +230,7 @@ export const TableOfContents = ({
|
||||||
horizontalLine = false,
|
horizontalLine = false,
|
||||||
}: TableOfContentsProps): JSX.Element => {
|
}: TableOfContentsProps): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const toc = useMemo(() => getTocFromMarkdawn(preprocessMarkDawn(text), title), [text, title]);
|
const toc = useMemo(() => getTocFromMarkdawn(preprocessMarkDawn(text), title), [text, title]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useRouter } from "next/router";
|
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 { Ico, Icon } from "components/Ico";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
|
@ -43,7 +43,6 @@ export const NavOption = ({
|
||||||
() => active || router.asPath.startsWith(url),
|
() => active || router.asPath.startsWith(url),
|
||||||
[active, router.asPath, url]
|
[active, router.asPath, url]
|
||||||
);
|
);
|
||||||
const [isFocused, setFocused] = useState(false);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
@ -65,11 +64,8 @@ export const NavOption = ({
|
||||||
border={border}
|
border={border}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
active={isActive}
|
active={isActive}
|
||||||
disabled={disabled}
|
disabled={disabled}>
|
||||||
onFocusChanged={setFocused}>
|
{icon && <Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive} />}
|
||||||
{icon && (
|
|
||||||
<Ico icon={icon} className="mt-[-.1em] !text-2xl" isFilled={isActive || isFocused} />
|
|
||||||
)}
|
|
||||||
{!reduced && (
|
{!reduced && (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-2xl">{title}</h3>
|
<h3 className="text-2xl">{title}</h3>
|
||||||
|
|
|
@ -1,12 +1,11 @@
|
||||||
import { useCallback } from "react";
|
import { useCallback } from "react";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { TranslatedProps } from "types/TranslatedProps";
|
import { TranslatedProps } from "types/TranslatedProps";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { isDefined } from "helpers/others";
|
import { isDefined } from "helpers/others";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -24,9 +23,8 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props): JSX.Element => {
|
||||||
const { setSubPanelOpen } = useAppLayout();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { langui } = useLocalData();
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const { is3ColumnsLayout } = useContainerQueries();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -34,12 +32,7 @@ export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props):
|
||||||
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
||||||
!isDefined(displayOnlyOn)) && (
|
!isDefined(displayOnlyOn)) && (
|
||||||
<div className={className}>
|
<div className={className}>
|
||||||
<Button
|
<Button href={href} text={`${langui.return_to} ${title}`} icon={Icon.NavigateBefore} />
|
||||||
onClick={() => setSubPanelOpen(false)}
|
|
||||||
href={href}
|
|
||||||
text={`${langui.return_to} ${title}`}
|
|
||||||
icon={Icon.NavigateBefore}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -3,15 +3,14 @@ import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { NavOption } from "components/PanelComponents/NavOption";
|
import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
import { ToolTip } from "components/ToolTip";
|
import { ToolTip } from "components/ToolTip";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { Link } from "components/Inputs/Link";
|
import { Link } from "components/Inputs/Link";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
|
||||||
import { ColoredSvg } from "components/ColoredSvg";
|
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 => {
|
export const MainPanel = (): JSX.Element => {
|
||||||
const { is3ColumnsLayout } = useContainerQueries();
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const { mainPanelReduced, toggleMainPanelReduced, setConfigPanelOpen } = useAppLayout();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { langui } = useLocalData();
|
const [isMainPanelReduced, setMainPanelReduced] = useAtomPair(atoms.layout.mainPanelReduced);
|
||||||
|
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"grid content-start justify-center gap-y-2 p-8 text-center",
|
"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 */}
|
{/* Reduce/expand main menu */}
|
||||||
{is3ColumnsLayout && (
|
{is3ColumnsLayout && (
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"fixed top-1/2",
|
"fixed top-1/2",
|
||||||
cIf(mainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
|
cIf(isMainPanelReduced, "left-[4.65rem]", "left-[18.65rem]")
|
||||||
)}>
|
)}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (mainPanelReduced) {
|
if (isMainPanelReduced) {
|
||||||
sendAnalytics("MainPanel", "Expand");
|
sendAnalytics("MainPanel", "Expand");
|
||||||
} else {
|
} else {
|
||||||
sendAnalytics("MainPanel", "Reduce");
|
sendAnalytics("MainPanel", "Reduce");
|
||||||
}
|
}
|
||||||
toggleMainPanelReduced();
|
setMainPanelReduced((current) => !current);
|
||||||
}}
|
}}
|
||||||
className="z-50 bg-light !px-2"
|
className="z-50 bg-light !px-2"
|
||||||
icon={mainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft}
|
icon={isMainPanelReduced ? Icon.ChevronRight : Icon.ChevronLeft}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -57,28 +57,28 @@ export const MainPanel = (): JSX.Element => {
|
||||||
src="/icons/accords.svg"
|
src="/icons/accords.svg"
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"mb-4 aspect-square bg-black hover:bg-dark",
|
"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>
|
</Link>
|
||||||
|
|
||||||
{(!mainPanelReduced || !is3ColumnsLayout) && (
|
{(!isMainPanelReduced || !is3ColumnsLayout) && (
|
||||||
<h2 className="mb-4 text-3xl">Accord’s Library</h2>
|
<h2 className="mb-4 text-3xl">Accord’s Library</h2>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
"flex flex-wrap gap-2",
|
"flex flex-wrap gap-2",
|
||||||
cIf(mainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
cIf(isMainPanelReduced && is3ColumnsLayout, "flex-col gap-3", "flex-row")
|
||||||
)}>
|
)}>
|
||||||
<ToolTip
|
<ToolTip
|
||||||
content={<h3 className="text-2xl">{langui.open_settings}</h3>}
|
content={<h3 className="text-2xl">{langui.open_settings}</h3>}
|
||||||
placement="right"
|
placement="right"
|
||||||
className="text-left"
|
className="text-left"
|
||||||
disabled={!mainPanelReduced}>
|
disabled={!isMainPanelReduced}>
|
||||||
<Button
|
<Button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setConfigPanelOpen(true);
|
setSettingsOpened(true);
|
||||||
sendAnalytics("Settings", "Open settings");
|
sendAnalytics("Settings", "Open settings");
|
||||||
}}
|
}}
|
||||||
icon={Icon.Settings}
|
icon={Icon.Settings}
|
||||||
|
@ -95,7 +95,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
icon={Icon.LibraryBooks}
|
icon={Icon.LibraryBooks}
|
||||||
title={langui.library}
|
title={langui.library}
|
||||||
subtitle={langui.library_short_description}
|
subtitle={langui.library_short_description}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
|
@ -103,7 +103,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
icon={Icon.Workspaces}
|
icon={Icon.Workspaces}
|
||||||
title={langui.contents}
|
title={langui.contents}
|
||||||
subtitle={langui.contents_short_description}
|
subtitle={langui.contents_short_description}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
|
@ -111,7 +111,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
icon={Icon.TravelExplore}
|
icon={Icon.TravelExplore}
|
||||||
title={langui.wiki}
|
title={langui.wiki}
|
||||||
subtitle={langui.wiki_short_description}
|
subtitle={langui.wiki_short_description}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
|
@ -119,7 +119,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
icon={Icon.WatchLater}
|
icon={Icon.WatchLater}
|
||||||
title={langui.chronicles}
|
title={langui.chronicles}
|
||||||
subtitle={langui.chronicles_short_description}
|
subtitle={langui.chronicles_short_description}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
@ -128,7 +128,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
url="/news"
|
url="/news"
|
||||||
icon={Icon.Feed}
|
icon={Icon.Feed}
|
||||||
title={langui.news}
|
title={langui.news}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/*
|
{/*
|
||||||
|
@ -136,7 +136,7 @@ export const MainPanel = (): JSX.Element => {
|
||||||
url="/merch"
|
url="/merch"
|
||||||
icon={Icon.Store}
|
icon={Icon.Store}
|
||||||
title={langui.merch}
|
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/"
|
url="https://gallery.accords-library.com/posts/"
|
||||||
icon={Icon.Collections}
|
icon={Icon.Collections}
|
||||||
title={langui.gallery}
|
title={langui.gallery}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/archives"
|
url="/archives"
|
||||||
icon={Icon.Inventory2}
|
icon={Icon.Inventory2}
|
||||||
title={langui.archives}
|
title={langui.archives}
|
||||||
reduced={mainPanelReduced && is3ColumnsLayout}
|
reduced={isMainPanelReduced && is3ColumnsLayout}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<NavOption
|
<NavOption
|
||||||
url="/about-us"
|
url="/about-us"
|
||||||
icon={Icon.Info}
|
icon={Icon.Info}
|
||||||
title={langui.about_us}
|
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) && (
|
{isDefinedAndNotEmpty(langui.licensing_notice) && (
|
||||||
<p>
|
<p>
|
||||||
<Markdown>{langui.licensing_notice}</Markdown>
|
<Markdown>{langui.licensing_notice}</Markdown>
|
||||||
|
|
|
@ -7,37 +7,32 @@ import { OrderableList } from "components/Inputs/OrderableList";
|
||||||
import { Select } from "components/Inputs/Select";
|
import { Select } from "components/Inputs/Select";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { Popup } from "components/Containers/Popup";
|
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 { sendAnalytics } from "helpers/analytics";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { prettyLanguage } from "helpers/formatters";
|
import { prettyLanguage } from "helpers/formatters";
|
||||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
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 => {
|
export const SettingsPopup = (): JSX.Element => {
|
||||||
const {
|
const [preferredLanguages, setPreferredLanguages] = useAtomPair(
|
||||||
currency,
|
atoms.settings.preferredLanguages
|
||||||
darkMode,
|
);
|
||||||
dyslexic,
|
const [isSettingsOpened, setSettingsOpened] = useAtomPair(atoms.layout.settingsOpened);
|
||||||
fontSize,
|
const [currency, setCurrency] = useAtomPair(atoms.settings.currency);
|
||||||
playerName,
|
const [isDyslexic, setDyslexic] = useAtomPair(atoms.settings.dyslexic);
|
||||||
preferredLanguages,
|
const [fontSize, setFontSize] = useAtomPair(atoms.settings.fontSize);
|
||||||
selectedThemeMode,
|
const [playerName, setPlayerName] = useAtomPair(atoms.settings.playerName);
|
||||||
setCurrency,
|
const [themeMode, setThemeMode] = useAtomPair(atoms.settings.themeMode);
|
||||||
setDarkMode,
|
|
||||||
setDyslexic,
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
setFontSize,
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
setPlayerName,
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
setPreferredLanguages,
|
|
||||||
setSelectedThemeMode,
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
} = useUserSettings();
|
|
||||||
|
|
||||||
const { langui, currencies, languages } = useLocalData();
|
|
||||||
const { is1ColumnLayout } = useContainerQueries();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { configPanelOpen, setConfigPanelOpen } = useAppLayout();
|
|
||||||
|
|
||||||
const currencyOptions = useMemo(
|
const currencyOptions = useMemo(
|
||||||
() =>
|
() =>
|
||||||
|
@ -59,9 +54,9 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Popup
|
<Popup
|
||||||
isVisible={configPanelOpen}
|
isVisible={isSettingsOpened}
|
||||||
onCloseRequest={() => {
|
onCloseRequest={() => {
|
||||||
setConfigPanelOpen(false);
|
setSettingsOpened(false);
|
||||||
sendAnalytics("Settings", "Close settings");
|
sendAnalytics("Settings", "Close settings");
|
||||||
}}>
|
}}>
|
||||||
<h2 className="text-2xl">{langui.settings}</h2>
|
<h2 className="text-2xl">{langui.settings}</h2>
|
||||||
|
@ -110,28 +105,26 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
buttonsProps={[
|
buttonsProps={[
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setDarkMode(false);
|
setThemeMode(ThemeMode.Light);
|
||||||
setSelectedThemeMode(true);
|
|
||||||
sendAnalytics("Settings", "Change theme (light)");
|
sendAnalytics("Settings", "Change theme (light)");
|
||||||
},
|
},
|
||||||
active: selectedThemeMode && !darkMode,
|
active: themeMode === ThemeMode.Light,
|
||||||
text: langui.light,
|
text: langui.light,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setSelectedThemeMode(false);
|
setThemeMode(ThemeMode.Auto);
|
||||||
sendAnalytics("Settings", "Change theme (auto)");
|
sendAnalytics("Settings", "Change theme (auto)");
|
||||||
},
|
},
|
||||||
active: !selectedThemeMode,
|
active: themeMode === ThemeMode.Auto,
|
||||||
text: langui.auto,
|
text: langui.auto,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
setDarkMode(true);
|
setThemeMode(ThemeMode.Dark);
|
||||||
setSelectedThemeMode(true);
|
|
||||||
sendAnalytics("Settings", "Change theme (dark)");
|
sendAnalytics("Settings", "Change theme (dark)");
|
||||||
},
|
},
|
||||||
active: selectedThemeMode && darkMode,
|
active: themeMode === ThemeMode.Dark,
|
||||||
text: langui.dark,
|
text: langui.dark,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
@ -198,7 +191,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
<h3 className="text-xl">{langui.font}</h3>
|
<h3 className="text-xl">{langui.font}</h3>
|
||||||
<div className="grid gap-2">
|
<div className="grid gap-2">
|
||||||
<Button
|
<Button
|
||||||
active={!dyslexic}
|
active={!isDyslexic}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDyslexic(false);
|
setDyslexic(false);
|
||||||
sendAnalytics("Settings", "Change font (Zen Maru Gothic)");
|
sendAnalytics("Settings", "Change font (Zen Maru Gothic)");
|
||||||
|
@ -207,7 +200,7 @@ export const SettingsPopup = (): JSX.Element => {
|
||||||
text="Zen Maru Gothic"
|
text="Zen Maru Gothic"
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
active={dyslexic}
|
active={isDyslexic}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setDyslexic(true);
|
setDyslexic(true);
|
||||||
sendAnalytics("Settings", "Change font (OpenDyslexic)");
|
sendAnalytics("Settings", "Change font (OpenDyslexic)");
|
||||||
|
|
|
@ -13,7 +13,8 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { PostWithTranslations } from "types/types";
|
import { PostWithTranslations } from "types/types";
|
||||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||||
import { prettySlug } from "helpers/formatters";
|
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,
|
displayTitle = true,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: post.translations,
|
items: post.translations,
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
|
|
@ -11,8 +11,8 @@ import { ImageQuality } from "helpers/img";
|
||||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { TranslatedProps } from "types/TranslatedProps";
|
import { TranslatedProps } from "types/TranslatedProps";
|
||||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -70,8 +70,8 @@ export const PreviewCard = ({
|
||||||
infoAppend,
|
infoAppend,
|
||||||
disabled = false,
|
disabled = false,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { currency } = useUserSettings();
|
const currency = useAtomGetter(atoms.settings.currency);
|
||||||
const { currencies } = useLocalData();
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
const isHoverable = useDeviceSupportsHover();
|
const isHoverable = useDeviceSupportsHover();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ import { Chip } from "components/Chip";
|
||||||
import { RecorderChipFragment } from "graphql/generated";
|
import { RecorderChipFragment } from "graphql/generated";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes } from "helpers/others";
|
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 => {
|
export const RecorderChip = ({ recorder }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToolTip
|
<ToolTip
|
||||||
|
|
|
@ -8,8 +8,8 @@ import { cJoin } from "helpers/className";
|
||||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
interface Group<T> {
|
interface Group<T> {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -71,7 +71,7 @@ export const SmartList = <T,>({
|
||||||
className,
|
className,
|
||||||
}: Props<T>): JSX.Element => {
|
}: Props<T>): JSX.Element => {
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
useScrollTopOnChange(Ids.ContentPanel, [page], paginationScroolTop);
|
useScrollTopOnChange(Ids.ContentPanel, [page], paginationScroolTop);
|
||||||
useEffect(() => setPage(0), [searchingTerm, groupingFunction, groupSortingFunction, items]);
|
useEffect(() => setPage(0), [searchingTerm, groupingFunction, groupSortingFunction, items]);
|
||||||
|
|
||||||
|
@ -228,8 +228,8 @@ export const SmartList = <T,>({
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const DefaultRenderWhenEmpty = () => {
|
const DefaultRenderWhenEmpty = () => {
|
||||||
const { is3ColumnsLayout } = useContainerQueries();
|
const is3ColumnsLayout = useAtomGetter(atoms.containerQueries.is3ColumnsLayout);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<div className="grid h-full place-content-center">
|
<div className="grid h-full place-content-center">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
||||||
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||||
import { ImageQuality } from "helpers/img";
|
import { ImageQuality } from "helpers/img";
|
||||||
import { filterHasAttributes } from "helpers/others";
|
import { filterHasAttributes } from "helpers/others";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { useLightBox } from "contexts/LightBoxContext";
|
import { atoms } from "contexts/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -42,8 +42,8 @@ export const ThumbnailHeader = ({
|
||||||
description,
|
description,
|
||||||
languageSwitcher,
|
languageSwitcher,
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { showLightBox } = useLightBox();
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -5,8 +5,8 @@ import { getStatusDescription } from "helpers/others";
|
||||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { Button } from "components/Inputs/Button";
|
import { Button } from "components/Inputs/Button";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -30,8 +30,8 @@ interface Props {
|
||||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||||
|
|
||||||
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
const DefinitionCard = ({ source, translations = [], index, categories }: Props): JSX.Element => {
|
||||||
const { isContentPanelAtLeastMd } = useContainerQueries();
|
const isContentPanelAtLeastMd = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastMd);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: translations,
|
items: translations,
|
||||||
languageExtractor: useCallback((item: Props["translations"][number]) => item.language, []),
|
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 {
|
try {
|
||||||
umami(`[${category}] ${event}`);
|
umami(`[${category}] ${event}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
if (error instanceof ReferenceError) return;
|
||||||
console.log(error);
|
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 { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||||
import { filterDefined, isDefined } from "helpers/others";
|
import { filterDefined, isDefined } from "helpers/others";
|
||||||
import { getPreferredLanguage } from "helpers/locales";
|
import { getPreferredLanguage } from "helpers/locales";
|
||||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
interface Props<T> {
|
interface Props<T> {
|
||||||
items: T[];
|
items: T[];
|
||||||
|
@ -17,8 +17,8 @@ export const useSmartLanguage = <T>({
|
||||||
languageExtractor,
|
languageExtractor,
|
||||||
transform = (item) => item,
|
transform = (item) => item,
|
||||||
}: Props<T>): [T | undefined, typeof LanguageSwitcher, Parameters<typeof LanguageSwitcher>[0]] => {
|
}: Props<T>): [T | undefined, typeof LanguageSwitcher, Parameters<typeof LanguageSwitcher>[0]] => {
|
||||||
const { preferredLanguages } = useUserSettings();
|
const preferredLanguages = useAtomGetter(atoms.settings.preferredLanguages);
|
||||||
const { languages } = useLocalData();
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const availableLocales = useMemo(() => {
|
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 { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Img } from "components/Img";
|
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 {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
const FourOhFour = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
contentPanel={
|
contentPanel={
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { ContentPanel } from "components/Containers/ContentPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Img } from "components/Img";
|
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 {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
const FiveHundred = ({ openGraph, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
contentPanel={
|
contentPanel={
|
||||||
|
|
|
@ -9,7 +9,6 @@ import "@fontsource/zen-maru-gothic/900.css";
|
||||||
|
|
||||||
import type { AppProps } from "next/app";
|
import type { AppProps } from "next/app";
|
||||||
import Script from "next/script";
|
import Script from "next/script";
|
||||||
import { AppContextProvider } from "contexts/AppLayoutContext";
|
|
||||||
|
|
||||||
import "styles/debug.css";
|
import "styles/debug.css";
|
||||||
import "styles/formatted.css";
|
import "styles/formatted.css";
|
||||||
|
@ -17,19 +16,23 @@ import "styles/others.css";
|
||||||
import "styles/rc-slider.css";
|
import "styles/rc-slider.css";
|
||||||
import "styles/tippy.css";
|
import "styles/tippy.css";
|
||||||
|
|
||||||
import { TerminalContextProvider } from "contexts/TerminalContext";
|
import { useLocalData } from "contexts/localData";
|
||||||
import { UserSettingsProvider as UserSettingsContextProvider } from "contexts/UserSettingsContext";
|
import { useAppLayout } from "contexts/appLayout";
|
||||||
import { LocalDataContextProvider } from "contexts/LocalDataContext";
|
import { LightBoxProvider } from "contexts/LightBoxProvider";
|
||||||
import { ContainerQueriesContextProvider } from "contexts/ContainerQueriesContext";
|
import { SettingsPopup } from "components/Panels/SettingsPopup";
|
||||||
import { LightBoxContextProvider } from "contexts/LightBoxContext";
|
import { useSettings } from "contexts/settings";
|
||||||
|
import { useContainerQueries } from "contexts/containerQueries";
|
||||||
|
|
||||||
const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
const AccordsLibraryApp = (props: AppProps): JSX.Element => {
|
||||||
<LocalDataContextProvider>
|
useLocalData();
|
||||||
<AppContextProvider>
|
useAppLayout();
|
||||||
<UserSettingsContextProvider>
|
useSettings();
|
||||||
<ContainerQueriesContextProvider>
|
useContainerQueries();
|
||||||
<TerminalContextProvider>
|
|
||||||
<LightBoxContextProvider>
|
return (
|
||||||
|
<>
|
||||||
|
<SettingsPopup />
|
||||||
|
<LightBoxProvider />
|
||||||
<Script
|
<Script
|
||||||
async
|
async
|
||||||
defer
|
defer
|
||||||
|
@ -37,11 +40,7 @@ const AccordsLibraryApp = (props: AppProps): JSX.Element => (
|
||||||
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/umami.js`}
|
||||||
/>
|
/>
|
||||||
<props.Component {...props.pageProps} />
|
<props.Component {...props.pageProps} />
|
||||||
</LightBoxContextProvider>
|
</>
|
||||||
</TerminalContextProvider>
|
|
||||||
</ContainerQueriesContextProvider>
|
|
||||||
</UserSettingsContextProvider>
|
|
||||||
</AppContextProvider>
|
|
||||||
</LocalDataContextProvider>
|
|
||||||
);
|
);
|
||||||
|
};
|
||||||
export default AccordsLibraryApp;
|
export default AccordsLibraryApp;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
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 AccordsHandbook = (props: PostStaticProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -7,8 +7,8 @@ import { cIf, cJoin } from "helpers/className";
|
||||||
import { randomInt } from "helpers/numbers";
|
import { randomInt } from "helpers/numbers";
|
||||||
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
import { RequestMailProps, ResponseMailProps } from "pages/api/mail";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -17,8 +17,8 @@ import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
||||||
|
|
||||||
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
const AboutUs = (props: PostStaticProps): JSX.Element => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { is1ColumnLayout } = useContainerQueries();
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const [formResponse, setFormResponse] = useState("");
|
const [formResponse, setFormResponse] = useState("");
|
||||||
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
const [formState, setFormState] = useState<"completed" | "ongoing" | "stale">("stale");
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@ import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const AboutUs = (props: Props): JSX.Element => {
|
const AboutUs = (props: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
subPanel={
|
subPanel={
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
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 Legality = (props: PostStaticProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { PostPage } from "components/PostPage";
|
import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
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 SharingPolicy = (props: PostStaticProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<PostPage
|
<PostPage
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { Icon } from "components/Ico";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 {}
|
interface Props extends AppLayoutRequired {}
|
||||||
|
|
||||||
const Archives = (props: Props): JSX.Element => {
|
const Archives = (props: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
|
|
|
@ -22,8 +22,8 @@ import { SmartList } from "components/SmartList";
|
||||||
import { cIf } from "helpers/className";
|
import { cIf } from "helpers/className";
|
||||||
import { TextInput } from "components/Inputs/TextInput";
|
import { TextInput } from "components/Inputs/TextInput";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -45,9 +45,9 @@ interface Props extends AppLayoutRequired {
|
||||||
|
|
||||||
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
const Channel = ({ channel, ...otherProps }: Props): JSX.Element => {
|
||||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ import { compareDate } from "helpers/date";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { cIf } from "helpers/className";
|
import { cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -44,9 +44,9 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
|
const Videos = ({ videos, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(true);
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import { NavOption } from "components/PanelComponents/NavOption";
|
||||||
import { ReturnButton } from "components/PanelComponents/ReturnButton";
|
import { ReturnButton } from "components/PanelComponents/ReturnButton";
|
||||||
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
||||||
import { SubPanel } from "components/Containers/SubPanel";
|
import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { useAppLayout } from "contexts/AppLayoutContext";
|
|
||||||
import { GetVideoQuery } from "graphql/generated";
|
import { GetVideoQuery } from "graphql/generated";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
||||||
|
@ -18,8 +17,8 @@ import { filterHasAttributes, isDefined } from "helpers/others";
|
||||||
import { getVideoFile } from "helpers/videos";
|
import { getVideoFile } from "helpers/videos";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -31,9 +30,8 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
const { setSubPanelOpen } = useAppLayout();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { langui } = useLocalData();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
|
@ -47,29 +45,12 @@ const Video = ({ video, ...otherProps }: Props): JSX.Element => {
|
||||||
|
|
||||||
<HorizontalLine />
|
<HorizontalLine />
|
||||||
|
|
||||||
<NavOption
|
<NavOption title={langui.video} url="#video" border />
|
||||||
title={langui.video}
|
<NavOption title={langui.channel} url="#channel" border />
|
||||||
url="#video"
|
<NavOption title={langui.description} url="#description" border />
|
||||||
border
|
|
||||||
onClick={() => setSubPanelOpen(false)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NavOption
|
|
||||||
title={langui.channel}
|
|
||||||
url="#channel"
|
|
||||||
border
|
|
||||||
onClick={() => setSubPanelOpen(false)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<NavOption
|
|
||||||
title={langui.description}
|
|
||||||
url="#description"
|
|
||||||
border
|
|
||||||
onClick={() => setSubPanelOpen(false)}
|
|
||||||
/>
|
|
||||||
</SubPanel>
|
</SubPanel>
|
||||||
),
|
),
|
||||||
[setSubPanelOpen, langui]
|
[langui]
|
||||||
);
|
);
|
||||||
|
|
||||||
const contentPanel = useMemo(
|
const contentPanel = useMemo(
|
||||||
|
|
|
@ -19,7 +19,8 @@ import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/local
|
||||||
import { getDescription } from "helpers/description";
|
import { getDescription } from "helpers/description";
|
||||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 Chronicle = ({ chronicle, chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: chronicle.translations,
|
items: chronicle.translations,
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
|
|
@ -12,7 +12,8 @@ import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 Chronicles = ({ chapters, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
<SubPanel>
|
<SubPanel>
|
||||||
|
|
|
@ -32,8 +32,8 @@ import { TranslatedPreviewLine } from "components/PreviewLine";
|
||||||
import { cIf } from "helpers/className";
|
import { cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -45,8 +45,11 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
const Content = ({ content, ...otherProps }: Props): JSX.Element => {
|
||||||
const { isContentPanelAtLeast2xl, is1ColumnLayout } = useContainerQueries();
|
const isContentPanelAtLeast2xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast2xl);
|
||||||
const { langui, languages } = useLocalData();
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
|
|
||||||
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
|
const languages = useAtomGetter(atoms.localData.languages);
|
||||||
|
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: content.translations,
|
items: content.translations,
|
||||||
|
|
|
@ -25,8 +25,8 @@ import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -50,8 +50,8 @@ interface Props extends AppLayoutRequired {
|
||||||
|
|
||||||
const Contents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
const Contents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const [groupingMethod, setGroupingMethod] = useState<number>(
|
const [groupingMethod, setGroupingMethod] = useState<number>(
|
||||||
DEFAULT_FILTERS_STATE.groupingMethod
|
DEFAULT_FILTERS_STATE.groupingMethod
|
||||||
|
|
|
@ -18,8 +18,8 @@ import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { cJoin, cIf } from "helpers/className";
|
import { cJoin, cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
|
import { TranslatedPreviewFolder } from "components/Contents/PreviewFolder";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -34,8 +34,8 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
const ContentsFolder = ({ openGraph, folder, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
@ -273,7 +273,7 @@ export const getStaticPaths: GetStaticPaths = async (context) => {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const NoContentNorFolderMessage = () => {
|
const NoContentNorFolderMessage = () => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<div className="grid place-content-center">
|
<div className="grid place-content-center">
|
||||||
<div
|
<div
|
||||||
|
|
|
@ -936,13 +936,8 @@ const LightThemeSection = ({ className, children }: ThemedSectionProps) => (
|
||||||
);
|
);
|
||||||
|
|
||||||
const WhiteSection = ({ className, children }: ThemedSectionProps) => (
|
const WhiteSection = ({ className, children }: ThemedSectionProps) => (
|
||||||
<div
|
<div className="mb-12 rounded-xl bg-[white] py-10 px-14 drop-shadow-lg shadow-shade">
|
||||||
className={cJoin(
|
<div className={cJoin("text-black set-theme-light", className)}>{children}</div>
|
||||||
`mb-12 rounded-xl bg-[white] py-10 px-14 text-black drop-shadow-lg shadow-shade
|
|
||||||
set-theme-light`,
|
|
||||||
className
|
|
||||||
)}>
|
|
||||||
{children}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { Terminal } from "components/Cli/Terminal";
|
import { Terminal } from "components/Cli/Terminal";
|
||||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -11,8 +11,8 @@ import { useLocalData } from "contexts/LocalDataContext";
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const Home = ({ ...otherProps }: PostStaticProps): JSX.Element => {
|
const Home = ({ ...otherProps }: PostStaticProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const isTerminalMode = useIsTerminalMode();
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
|
|
||||||
if (isTerminalMode) {
|
if (isTerminalMode) {
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -51,10 +51,8 @@ import { useIntersectionList } from "hooks/useIntersectionList";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Ids } from "types/ids";
|
import { Ids } from "types/ids";
|
||||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|
||||||
import { useLightBox } from "contexts/LightBoxContext";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -74,14 +72,18 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
|
||||||
const { currency } = useUserSettings();
|
const currency = useAtomGetter(atoms.settings.currency);
|
||||||
const { langui, currencies } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { isContentPanelAtLeast3xl, isContentPanelAtLeastSm } = useContainerQueries();
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
|
|
||||||
|
const isContentPanelAtLeast3xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast3xl);
|
||||||
|
const isContentPanelAtLeastSm = useAtomGetter(atoms.containerQueries.isContentPanelAtLeastSm);
|
||||||
|
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(false);
|
const { value: keepInfoVisible, toggle: toggleKeepInfoVisible } = useBoolean(false);
|
||||||
|
|
||||||
const { showLightBox } = useLightBox();
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
|
|
||||||
useScrollTopOnChange(Ids.ContentPanel, [item]);
|
useScrollTopOnChange(Ids.ContentPanel, [item]);
|
||||||
const currentIntersection = useIntersectionList(intersectionIds);
|
const currentIntersection = useIntersectionList(intersectionIds);
|
||||||
|
@ -692,7 +694,7 @@ const ContentLine = ({
|
||||||
parentSlug,
|
parentSlug,
|
||||||
condensed,
|
condensed,
|
||||||
}: ContentLineProps): JSX.Element => {
|
}: ContentLineProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
|
const { value: isOpened, toggle: toggleOpened } = useBoolean(false);
|
||||||
const [selectedTranslation] = useSmartLanguage({
|
const [selectedTranslation] = useSmartLanguage({
|
||||||
items: content?.translations ?? [],
|
items: content?.translations ?? [],
|
||||||
|
|
|
@ -41,10 +41,9 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||||
import { TranslatedProps } from "types/TranslatedProps";
|
import { TranslatedProps } from "types/TranslatedProps";
|
||||||
import { prettyInlineTitle, prettySlug } from "helpers/formatters";
|
import { prettyInlineTitle, prettySlug } from "helpers/formatters";
|
||||||
import { useFullscreen } from "hooks/useFullscreen";
|
import { useFullscreen } from "hooks/useFullscreen";
|
||||||
import { useUserSettings } from "contexts/UserSettingsContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|
||||||
|
|
||||||
const CUSTOM_DARK_DROPSHADOW = `
|
const CUSTOM_DARK_DROPSHADOW = `
|
||||||
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
|
drop-shadow(0 0 0.5em rgb(var(--theme-color-shade) / 30%))
|
||||||
|
@ -90,9 +89,9 @@ const LibrarySlug = ({
|
||||||
item,
|
item,
|
||||||
...otherProps
|
...otherProps
|
||||||
}: Props): JSX.Element => {
|
}: Props): JSX.Element => {
|
||||||
const { is1ColumnLayout } = useContainerQueries();
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { darkMode } = useUserSettings();
|
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
||||||
const {
|
const {
|
||||||
filterSettings,
|
filterSettings,
|
||||||
isSidePagesEnabled,
|
isSidePagesEnabled,
|
||||||
|
@ -453,7 +452,7 @@ const LibrarySlug = ({
|
||||||
display: "grid",
|
display: "grid",
|
||||||
placeContent: "center",
|
placeContent: "center",
|
||||||
filter: filterSettings.dropShadow
|
filter: filterSettings.dropShadow
|
||||||
? darkMode
|
? isDarkMode
|
||||||
? CUSTOM_DARK_DROPSHADOW
|
? CUSTOM_DARK_DROPSHADOW
|
||||||
: CUSTOM_LIGHT_DROPSHADOW
|
: CUSTOM_LIGHT_DROPSHADOW
|
||||||
: undefined,
|
: undefined,
|
||||||
|
@ -631,7 +630,7 @@ const LibrarySlug = ({
|
||||||
is1ColumnLayout,
|
is1ColumnLayout,
|
||||||
currentZoom,
|
currentZoom,
|
||||||
filterSettings,
|
filterSettings,
|
||||||
darkMode,
|
isDarkMode,
|
||||||
pageHeight,
|
pageHeight,
|
||||||
effectiveDisplayMode,
|
effectiveDisplayMode,
|
||||||
firstPage,
|
firstPage,
|
||||||
|
@ -798,7 +797,7 @@ interface PageFiltersProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
||||||
const { darkMode } = useUserSettings();
|
const isDarkMode = useAtomGetter(atoms.settings.darkMode);
|
||||||
const commonCss = useMemo(
|
const commonCss = useMemo(
|
||||||
() => cJoin("absolute inset-0", cIf(page === "right", "[background-position-x:-100%]")),
|
() => cJoin("absolute inset-0", cIf(page === "right", "[background-position-x:-100%]")),
|
||||||
[page]
|
[page]
|
||||||
|
@ -823,7 +822,7 @@ const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
||||||
commonCss,
|
commonCss,
|
||||||
`bg-blend-lighten mix-blend-multiply [background-image:url(/reader/book-fold.webp)]
|
`bg-blend-lighten mix-blend-multiply [background-image:url(/reader/book-fold.webp)]
|
||||||
[background-size:200%_100%]`,
|
[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(
|
className={cJoin(
|
||||||
commonCss,
|
commonCss,
|
||||||
`bg-blend-lighten mix-blend-multiply [background-size:200%_100%]`,
|
`bg-blend-lighten mix-blend-multiply [background-size:200%_100%]`,
|
||||||
cIf(!darkMode, "bg-[#FFF]/50"),
|
cIf(!isDarkMode, "bg-[#FFF]/50"),
|
||||||
cIf(
|
cIf(
|
||||||
page === "single",
|
page === "single",
|
||||||
"[background-image:url(/reader/lighting-single-page.webp)]",
|
"[background-image:url(/reader/lighting-single-page.webp)]",
|
||||||
|
@ -846,7 +845,7 @@ const PageFilters = ({ page, bookType, options }: PageFiltersProps) => {
|
||||||
className={cJoin(
|
className={cJoin(
|
||||||
commonCss,
|
commonCss,
|
||||||
`bg-blend-lighten mix-blend-soft-light [background-size:200%_100%]`,
|
`bg-blend-lighten mix-blend-soft-light [background-size:200%_100%]`,
|
||||||
cIf(!darkMode, "bg-[#FFF]/30"),
|
cIf(!isDarkMode, "bg-[#FFF]/30"),
|
||||||
cIf(
|
cIf(
|
||||||
page === "single",
|
page === "single",
|
||||||
"[background-image:url(/reader/specular-single-page.webp)]",
|
"[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 ScanSet = ({ onClickOnImage, scanSet, id, title, content }: ScanSetProps): JSX.Element => {
|
||||||
const { is1ColumnLayout } = useContainerQueries();
|
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedScan, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: scanSet,
|
items: scanSet,
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
|
|
@ -31,9 +31,9 @@ import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { cIf, cJoin } from "helpers/className";
|
import { cIf, cJoin } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
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 { 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 Library = ({ items, ...otherProps }: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const { langui, currencies } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
|
const currencies = useAtomGetter(atoms.localData.currencies);
|
||||||
|
|
||||||
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
const { libraryItemUserStatus } = useLibraryItemUserStatus();
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
|
|
||||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,8 @@ import { SubPanel } from "components/Containers/SubPanel";
|
||||||
import { Icon } from "components/Ico";
|
import { Icon } from "components/Ico";
|
||||||
import { getOpenGraph } from "helpers/openGraph";
|
import { getOpenGraph } from "helpers/openGraph";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 {}
|
interface Props extends AppLayoutRequired {}
|
||||||
const Merch = (props: Props): JSX.Element => {
|
const Merch = (props: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
return (
|
return (
|
||||||
<AppLayout
|
<AppLayout
|
||||||
subPanel={
|
subPanel={
|
||||||
|
|
|
@ -4,14 +4,13 @@ import { PostPage } from "components/PostPage";
|
||||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||||
import { getReadySdk } from "graphql/sdk";
|
import { getReadySdk } from "graphql/sdk";
|
||||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
|
||||||
import { Terminal } from "components/Cli/Terminal";
|
import { Terminal } from "components/Cli/Terminal";
|
||||||
import { PostWithTranslations } from "types/types";
|
import { PostWithTranslations } from "types/types";
|
||||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||||
import { prettyTerminalBoxedTitle } from "helpers/terminal";
|
import { prettyTerminalBoxedTitle } from "helpers/terminal";
|
||||||
import { prettyMarkdown } from "helpers/description";
|
import { prettyMarkdown } from "helpers/description";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
* ──────────────────────────────────────────╯ PAGE ╰─────────────────────────────────────────────
|
||||||
|
@ -20,8 +19,8 @@ import { useLocalData } from "contexts/LocalDataContext";
|
||||||
interface Props extends PostStaticProps {}
|
interface Props extends PostStaticProps {}
|
||||||
|
|
||||||
const LibrarySlug = (props: Props): JSX.Element => {
|
const LibrarySlug = (props: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const isTerminalMode = useIsTerminalMode();
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
if (isTerminalMode) {
|
if (isTerminalMode) {
|
||||||
|
|
|
@ -23,10 +23,9 @@ import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { cIf } from "helpers/className";
|
import { cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
|
||||||
import { Terminal } from "components/Cli/Terminal";
|
import { Terminal } from "components/Cli/Terminal";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -48,8 +47,8 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
||||||
const {
|
const {
|
||||||
|
@ -57,7 +56,7 @@ const News = ({ posts, ...otherProps }: Props): JSX.Element => {
|
||||||
toggle: toggleKeepInfoVisible,
|
toggle: toggleKeepInfoVisible,
|
||||||
setValue: setKeepInfoVisible,
|
setValue: setKeepInfoVisible,
|
||||||
} = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
} = useBoolean(DEFAULT_FILTERS_STATE.keepInfoVisible);
|
||||||
const isTerminalMode = useIsTerminalMode();
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
|
|
||||||
const subPanel = useMemo(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|
|
@ -22,10 +22,8 @@ import { cIf, cJoin } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { Terminal } from "components/Cli/Terminal";
|
import { Terminal } from "components/Cli/Terminal";
|
||||||
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
import { prettyTerminalBoxedTitle, prettyTerminalUnderlinedTitle } from "helpers/terminal";
|
||||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|
||||||
import { useLightBox } from "contexts/LightBoxContext";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭────────╮
|
* ╭────────╮
|
||||||
|
@ -37,10 +35,10 @@ interface Props extends AppLayoutRequired {
|
||||||
}
|
}
|
||||||
|
|
||||||
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
const WikiPage = ({ page, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isTerminalMode = useIsTerminalMode();
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
const { showLightBox } = useLightBox();
|
const { showLightBox } = useAtomGetter(atoms.lightBox);
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: page.translations,
|
items: page.translations,
|
||||||
languageExtractor: useCallback(
|
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(
|
const subPanel = useMemo(
|
||||||
() => (
|
() => (
|
||||||
|
|
|
@ -31,7 +31,8 @@ import { TranslatedNavOption } from "components/PanelComponents/NavOption";
|
||||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||||
import { HorizontalLine } from "components/HorizontalLine";
|
import { HorizontalLine } from "components/HorizontalLine";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
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 Chronology = ({ chronologyItems, chronologyEras, ...otherProps }: Props): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const ids = useMemo(
|
const ids = useMemo(
|
||||||
() =>
|
() =>
|
||||||
filterHasAttributes(chronologyEras, ["attributes"] as const).map(
|
filterHasAttributes(chronologyEras, ["attributes"] as const).map(
|
||||||
|
@ -315,7 +316,7 @@ interface ChronologyEventProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Element => {
|
export const ChronologyEvent = ({ event, id }: ChronologyEventProps): JSX.Element => {
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
const [selectedTranslation, LanguageSwitcher, languageSwitcherProps] = useSmartLanguage({
|
||||||
items: event.translations ?? [],
|
items: event.translations ?? [],
|
||||||
languageExtractor: useCallback(
|
languageExtractor: useCallback(
|
||||||
|
|
|
@ -26,9 +26,8 @@ import { cIf } from "helpers/className";
|
||||||
import { getLangui } from "graphql/fetchLocalData";
|
import { getLangui } from "graphql/fetchLocalData";
|
||||||
import { sendAnalytics } from "helpers/analytics";
|
import { sendAnalytics } from "helpers/analytics";
|
||||||
import { Terminal } from "components/Cli/Terminal";
|
import { Terminal } from "components/Cli/Terminal";
|
||||||
import { useIsTerminalMode } from "hooks/useIsTerminalMode";
|
import { atoms } from "contexts/atoms";
|
||||||
import { useLocalData } from "contexts/LocalDataContext";
|
import { useAtomGetter } from "helpers/atoms";
|
||||||
import { useContainerQueries } from "contexts/ContainerQueriesContext";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ╭─────────────╮
|
* ╭─────────────╮
|
||||||
|
@ -52,9 +51,9 @@ interface Props extends AppLayoutRequired {
|
||||||
|
|
||||||
const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
|
const Wiki = ({ pages, ...otherProps }: Props): JSX.Element => {
|
||||||
const hoverable = useDeviceSupportsHover();
|
const hoverable = useDeviceSupportsHover();
|
||||||
const { langui } = useLocalData();
|
const langui = useAtomGetter(atoms.localData.langui);
|
||||||
const { isContentPanelAtLeast4xl } = useContainerQueries();
|
const isContentPanelAtLeast4xl = useAtomGetter(atoms.containerQueries.isContentPanelAtLeast4xl);
|
||||||
const isTerminalMode = useIsTerminalMode();
|
const isTerminalMode = useAtomGetter(atoms.layout.terminalMode);
|
||||||
|
|
||||||
const [searchName, setSearchName] = useState(DEFAULT_FILTERS_STATE.searchName);
|
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 {
|
export enum LibraryItemUserStatus {
|
||||||
None = 0,
|
None = 0,
|
||||||
Want = 1,
|
Want = 1,
|
||||||
|
|
Loading…
Reference in New Issue