Improved perf on all browser

This commit is contained in:
DrMint 2023-05-13 10:09:57 +02:00
parent 06d82e1133
commit 663bf4f08d
24 changed files with 275 additions and 114 deletions

View File

@ -187,7 +187,9 @@
"story_x": "Story {x}",
"player_name_tooltip": "Certain in-game texts use the player's name as part of the dialogue/narration. If you want to see your name in the transcript found on this website, feel free to enter your player's name here. If left empty, '(player)' will be used instead.",
"download_scans": "Download scans",
"search_placeholder": "Search..."
"search_placeholder": "Search...",
"performance_mode": "Performance mode",
"performance_mode_tooltip": "This option improves performances by reducing certain visual effects (blurry backgrounds, shadows...). This mode is enabled by default on Linux, iOS, and Safari. This mode cannot be disabled on iOS or Safari as the experience would be too poor."
}
},
{
@ -376,7 +378,9 @@
"story_x": "Histoire {x}",
"player_name_tooltip": "Certains textes dans les jeux utilisent le nom du joueur dans les dialogue/la narration. Si vous voulez voir votre nom dans les transcriptions se trouvant sur ce site web, n'hésitez pas à entrer votre nom de joueur ici. S'il n'est pas renseigné, '(player)' sera utilisé à la place.",
"download_scans": "Télécharger les scans",
"search_placeholder": "Rechercher ..."
"search_placeholder": "Rechercher ...",
"performance_mode": "Mode performance",
"performance_mode_tooltip": "Cette option permet d'améliorer les performances en réduisant certains effets visuels (flous, ombres...). Ce mode est activé par défaut sur Linux, iOS et Safari. Ce mode ne peut pas être désactivé sur iOS ou Safari car l'expérience serait trop mauvaise."
}
},
{
@ -565,7 +569,9 @@
"story_x": null,
"player_name_tooltip": null,
"download_scans": null,
"search_placeholder": null
"search_placeholder": null,
"performance_mode": null,
"performance_mode_tooltip": null
}
},
{
@ -754,7 +760,9 @@
"story_x": null,
"player_name_tooltip": null,
"download_scans": null,
"search_placeholder": null
"search_placeholder": null,
"performance_mode": null,
"performance_mode_tooltip": null
}
},
{
@ -943,7 +951,9 @@
"story_x": null,
"player_name_tooltip": null,
"download_scans": null,
"search_placeholder": null
"search_placeholder": null,
"performance_mode": null,
"performance_mode_tooltip": null
}
}
]

View File

@ -1,6 +1,7 @@
import Head from "next/head";
import { useSwipeable } from "react-swipeable";
import { MaterialSymbol } from "material-symbols";
import { atom } from "jotai";
import { layout } from "../../design.config";
import { Ico } from "./Ico";
import { MainPanel } from "./Panels/MainPanel";
@ -18,6 +19,7 @@ import { useFormat } from "hooks/useFormat";
*/
const SENSIBILITY_SWIPE = 1.1;
const isIOSAtom = atom((get) => get(atoms.userAgent.os) === "iOS");
/*
*
@ -48,11 +50,12 @@ export const AppLayout = ({
const [isSubPanelOpened, setSubPanelOpened] = useAtomPair(atoms.layout.subPanelOpened);
const [isMainPanelOpened, setMainPanelOpened] = useAtomPair(atoms.layout.mainPanelOpened);
const isMenuGesturesEnabled = useAtomGetter(atoms.layout.menuGesturesEnabled);
const { format } = useFormat();
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const is1ColumnLayout = useAtomGetter(atoms.containerQueries.is1ColumnLayout);
const isScreenAtLeastXs = useAtomGetter(atoms.containerQueries.isScreenAtLeastXs);
const isIOS = useAtomGetter(isIOSAtom);
const { format } = useFormat();
const handlers = useSwipeable({
onSwipedLeft: (SwipeEventData) => {
@ -121,7 +124,8 @@ export const AppLayout = ({
<div
id={Ids.ContentPanel}
className={cJoin(
"bg-light texture-paper-dots [grid-area:content]",
"bg-light [grid-area:content]",
cIf(!isIOS, "texture-paper-dots"),
cIf(contentPanelScroolbar, "overflow-y-scroll")
)}>
{isDefined(contentPanel) ? (
@ -140,7 +144,7 @@ export const AppLayout = ({
[grid-area:content]`,
cIf(
(isMainPanelOpened || isSubPanelOpened) && is1ColumnLayout,
"backdrop-blur",
cIf(!isPerfModeEnabled, "backdrop-blur"),
"pointer-events-none touch-none"
)
)}>
@ -164,7 +168,8 @@ export const AppLayout = ({
<div
className={cJoin(
`z-40 grid grid-cols-[5rem_1fr_5rem] place-items-center border-t
border-dotted border-black bg-light texture-paper-dots [grid-area:navbar]`,
border-dotted border-black bg-light [grid-area:navbar]`,
cIf(!isIOS, "texture-paper-dots"),
cIf(!is1ColumnLayout, "hidden")
)}>
<Ico
@ -202,7 +207,8 @@ export const AppLayout = ({
id={Ids.SubPanel}
className={cJoin(
`z-40 overflow-y-scroll border-r border-dark/50 bg-light
transition-transform duration-300 scrollbar-none texture-paper-dots`,
transition-transform duration-300 scrollbar-none`,
cIf(!isIOS, "texture-paper-dots"),
cIf(
is1ColumnLayout,
"justify-self-end border-r-0 [grid-area:content]",
@ -219,7 +225,8 @@ export const AppLayout = ({
<div
className={cJoin(
`z-40 overflow-y-scroll border-r border-dark/50 bg-light
transition-transform duration-300 scrollbar-none texture-paper-dots`,
transition-transform duration-300 scrollbar-none`,
cIf(!isIOS, "texture-paper-dots"),
cIf(is1ColumnLayout, "justify-self-start [grid-area:content]", "[grid-area:main]"),
cIf(is1ColumnLayout && isScreenAtLeastXs, "w-[min(30rem,90%)]"),
cIf(!isMainPanelOpened && is1ColumnLayout, "-translate-x-full")

View File

@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
import { useHotkeys } from "react-hotkeys-hook";
import { cIf, cJoin } from "helpers/className";
import { atoms } from "contexts/atoms";
import { useAtomSetter } from "helpers/atoms";
import { useAtomGetter, useAtomSetter } from "helpers/atoms";
import { Button } from "components/Inputs/Button";
/*
@ -16,7 +16,6 @@ interface Props {
isVisible: boolean;
children: React.ReactNode;
fillViewport?: boolean;
hideBackground?: boolean;
padding?: boolean;
withCloseButton?: boolean;
}
@ -29,13 +28,13 @@ export const Popup = ({
isVisible,
children,
fillViewport,
hideBackground = false,
padding = true,
withCloseButton = true,
}: Props): JSX.Element => {
const setMenuGesturesEnabled = useAtomSetter(atoms.layout.menuGesturesEnabled);
const [isHidden, setHidden] = useState(!isVisible);
const [isActuallyVisible, setActuallyVisible] = useState(isVisible && !isHidden);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
useHotkeys("escape", () => onCloseRequest?.(), { enabled: isVisible }, [onCloseRequest]);
@ -68,7 +67,8 @@ export const Popup = ({
<div
className={cJoin(
"fixed inset-0 z-50 grid place-content-center transition-filter duration-500",
cIf(isActuallyVisible, "backdrop-blur", "pointer-events-none touch-none")
cIf(!isActuallyVisible, "pointer-events-none touch-none"),
cIf(isActuallyVisible && !isPerfModeEnabled, "backdrop-blur")
)}>
<div
className={cJoin(
@ -80,15 +80,15 @@ export const Popup = ({
<div
className={cJoin(
"grid place-items-center gap-4 transition-transform",
`grid place-items-center gap-4 rounded-lg bg-light shadow-2xl transition-transform
shadow-shade`,
cIf(padding, "p-10"),
cIf(isActuallyVisible, "scale-100", "scale-0"),
cIf(
fillViewport,
"absolute inset-10 content-start overflow-scroll",
"relative max-h-[80vh] overflow-y-auto"
),
cIf(!hideBackground, "rounded-lg bg-light shadow-2xl shadow-shade")
)
)}>
{withCloseButton && (
<div className="absolute right-6 top-6">

View File

@ -1,6 +1,8 @@
import { MouseEventHandler, useState } from "react";
import { Link } from "components/Inputs/Link";
import { cIf, cJoin } from "helpers/className";
import { useAtomGetter } from "helpers/atoms";
import { atoms } from "contexts/atoms";
/*
*
@ -27,20 +29,24 @@ export const UpPressable = ({
onClick,
}: Props): JSX.Element => {
const [isFocused, setFocused] = useState(false);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
return (
<Link
href={href}
onFocusChanged={setFocused}
onClick={onClick}
className={cJoin(
`drop-shadow-lg transition-all duration-300 shadow-shade`,
"transition-all duration-300 !shadow-shade",
cIf(isPerfModeEnabled, "shadow-lg", "drop-shadow-lg"),
cIf(!noBackground, "overflow-hidden rounded-md bg-highlight"),
cIf(
disabled,
"cursor-not-allowed opacity-50 grayscale",
cJoin(
"cursor-pointer hover:scale-102 hover:drop-shadow-xl",
cIf(isFocused, "hover:scale-105 hover:drop-shadow-2xl hover:duration-100")
"cursor-pointer hover:scale-102",
cIf(isPerfModeEnabled, "hover:shadow-xl", "hover:drop-shadow-xl"),
cIf(isFocused, "hover:scale-105 hover:duration-100")
)
),
className

View File

@ -20,12 +20,13 @@ interface Props {
icon?: MaterialSymbol;
text?: string | null | undefined;
alwaysNewTab?: boolean;
onClick?: MouseEventHandler<HTMLDivElement>;
onMouseUp?: MouseEventHandler<HTMLDivElement>;
onClick?: MouseEventHandler<HTMLButtonElement>;
onMouseUp?: MouseEventHandler<HTMLButtonElement>;
draggable?: boolean;
badgeNumber?: number;
disabled?: boolean;
size?: "normal" | "small";
type?: "button" | "reset" | "submit";
}
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
@ -43,31 +44,31 @@ export const Button = ({
alwaysNewTab = false,
badgeNumber,
disabled,
type,
size = "normal",
}: Props): JSX.Element => (
<Link href={href} alwaysNewTab={alwaysNewTab} disabled={disabled}>
<div className="relative">
<div
<button
type={type}
draggable={draggable}
id={id}
onClick={(event) => !disabled && onClick?.(event)}
disabled={disabled}
onClick={(event) => onClick?.(event)}
onMouseUp={onMouseUp}
onFocus={(event) => event.target.blur()}
className={cJoin(
`group grid cursor-pointer select-none grid-flow-col place-content-center
`group grid w-full grid-flow-col place-content-center
place-items-center gap-2 rounded-full border border-dark
leading-none text-dark transition-all`,
leading-none text-dark transition-all disabled:cursor-not-allowed
disabled:opacity-50 disabled:grayscale`,
cIf(size === "small", "px-3 py-1 text-xs", "px-4 py-3"),
cIf(active, "!border-black bg-black !text-light drop-shadow-lg shadow-black"),
cIf(active, "!border-black bg-black !text-light shadow-lg shadow-black"),
cIf(
disabled,
"cursor-not-allowed opacity-50 grayscale",
cIf(
!active,
`shadow-shade hover:bg-dark hover:text-light hover:drop-shadow-lg
!disabled && !active,
`shadow-shade hover:bg-dark hover:text-light hover:shadow-lg hover:shadow-shade
active:hover:!border-black active:hover:bg-black active:hover:!text-light
active:hover:drop-shadow-lg active:hover:shadow-black`
)
active:hover:shadow-lg active:hover:shadow-black`
),
className
)}>
@ -91,7 +92,7 @@ export const Button = ({
/>
)}
{isDefinedAndNotEmpty(text) && <p className="-translate-y-[0.05em] text-center">{text}</p>}
</div>
</button>
</div>
</Link>
);

View File

@ -27,6 +27,7 @@ export const TextInput = forwardRef<HTMLInputElement, Props>(
className="w-full"
type="text"
name={name}
autoCapitalize="off"
value={value}
disabled={disabled}
placeholder={placeholder ?? undefined}

View File

@ -39,6 +39,7 @@ export const LightBox = ({
onPressNext,
}: Props): JSX.Element => {
const [currentZoom, setCurrentZoom] = useState(1);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const { isFullscreen, toggleFullscreen, exitFullscreen, requestFullscreen } = useFullscreen(
Ids.LightBox
);
@ -62,7 +63,7 @@ export const LightBox = ({
id={Ids.LightBox}
className={cJoin(
"fixed inset-0 z-50 grid place-content-center transition-filter duration-500",
cIf(isVisible, "backdrop-blur", "pointer-events-none touch-none")
cIf(isVisible, cIf(!isPerfModeEnabled, "backdrop-blur"), "pointer-events-none touch-none")
)}>
<div
className={cJoin(
@ -90,8 +91,10 @@ export const LightBox = ({
}}>
{isDefined(src) && (
<Img
className={`h-[calc(100vh-4rem)] w-full object-contain drop-shadow-2xl
shadow-shade`}
className={cJoin(
`h-[calc(100vh-4rem)] w-full object-contain`,
cIf(!isPerfModeEnabled, "drop-shadow-2xl shadow-shade")
)}
src={src}
quality={ImageQuality.Large}
/>

View File

@ -0,0 +1,52 @@
import { Popup } from "components/Containers/Popup";
import { Ico } from "components/Ico";
import { atoms } from "contexts/atoms";
import { sendAnalytics } from "helpers/analytics";
import { useAtomGetter, useAtomPair } from "helpers/atoms";
/*
*
* COMPONENT
*/
export const DebugPopup = (): JSX.Element => {
const [isDebugMenuOpened, setDebugMenuOpened] = useAtomPair(atoms.layout.debugMenuOpened);
const os = useAtomGetter(atoms.userAgent.os);
const browser = useAtomGetter(atoms.userAgent.browser);
const engine = useAtomGetter(atoms.userAgent.engine);
const deviceType = useAtomGetter(atoms.userAgent.deviceType);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const isPerfModeToggleable = useAtomGetter(atoms.settings.isPerfModeToggleable);
const perfMode = useAtomGetter(atoms.settings.perfMode);
return (
<Popup
isVisible={isDebugMenuOpened}
onCloseRequest={() => {
setDebugMenuOpened(false);
sendAnalytics("Debug", "Close debug menu");
}}>
<h2 className="inline-flex place-items-center gap-2 text-2xl">
<Ico icon="bug_report" isFilled />
Debug Menu
</h2>
<h3>User Agent</h3>
<div>
<p>OS: {os}</p>
<p>Device type: {deviceType ?? "undefined"}</p>
<p>Browser: {browser}</p>
<p>Engine: {engine}</p>
</div>
<h3>Settings</h3>
<div>
<p>Raw perf mode: {perfMode}</p>
<p>Perf mode: {isPerfModeEnabled ? "true" : "false"}</p>
<p>Perf mode toggleable: {isPerfModeToggleable ? "true" : "false"}</p>
</div>
</Popup>
);
};

View File

@ -23,9 +23,12 @@ export const MainPanel = (): JSX.Element => {
const { format } = useFormat();
const [isMainPanelReduced, setMainPanelReduced] = useAtomPair(atoms.layout.mainPanelReduced);
const setMainPanelOpened = useAtomSetter(atoms.layout.mainPanelOpened);
const [isSettingsOpened, setSettingsOpened] = useAtomPair(atoms.layout.settingsOpened);
const [isSearchOpened, setSearchOpened] = useAtomPair(atoms.layout.searchOpened);
const [isDebugMenuOpened, setDebugMenuOpened] = useAtomPair(atoms.layout.debugMenuOpened);
const isDebugMenuAvailable = useAtomGetter(atoms.layout.debugMenuAvailable);
const closeMainPanel = useCallback(() => setMainPanelOpened(false), [setMainPanelOpened]);
const setSettingsOpened = useAtomSetter(atoms.layout.settingsOpened);
const setSearchOpened = useAtomSetter(atoms.layout.searchOpened);
return (
<div
@ -82,6 +85,7 @@ export const MainPanel = (): JSX.Element => {
content={<h3 className="text-2xl">{format("open_settings")}</h3>}
placement={isMainPanelReduced ? "right" : "top"}>
<Button
active={isSettingsOpened}
onClick={() => {
closeMainPanel();
setSettingsOpened(true);
@ -94,6 +98,7 @@ export const MainPanel = (): JSX.Element => {
content={<h3 className="text-2xl">{format("open_search")}</h3>}
placement={isMainPanelReduced ? "right" : "top"}>
<Button
active={isSearchOpened}
onClick={() => {
closeMainPanel();
setSearchOpened(true);
@ -102,6 +107,21 @@ export const MainPanel = (): JSX.Element => {
icon="search"
/>
</ToolTip>
{isDebugMenuAvailable && (
<ToolTip
content={<h3 className="text-2xl">Debug menu</h3>}
placement={isMainPanelReduced ? "right" : "top"}>
<Button
active={isDebugMenuOpened}
onClick={() => {
closeMainPanel();
setDebugMenuOpened(true);
sendAnalytics("Debug", "Open debug menu");
}}
icon="bug_report"
/>
</ToolTip>
)}
</div>
</div>
</div>

View File

@ -11,11 +11,12 @@ import { cJoin, cIf } from "helpers/className";
import { prettyLanguage } from "helpers/formatters";
import { filterHasAttributes, isDefined } from "helpers/asserts";
import { atoms } from "contexts/atoms";
import { useAtomGetter, useAtomPair } from "helpers/atoms";
import { ThemeMode } from "contexts/settings";
import { useAtomGetter, useAtomPair, useAtomSetter } from "helpers/atoms";
import { PerfMode, ThemeMode } from "contexts/settings";
import { Ico } from "components/Ico";
import { useFormat } from "hooks/useFormat";
import { ToolTip } from "components/ToolTip";
import { Switch } from "components/Inputs/Switch";
/*
*
@ -32,6 +33,9 @@ export const SettingsPopup = (): JSX.Element => {
const [fontSize, setFontSize] = useAtomPair(atoms.settings.fontSize);
const [playerName, setPlayerName] = useAtomPair(atoms.settings.playerName);
const [themeMode, setThemeMode] = useAtomPair(atoms.settings.themeMode);
const setPerfMode = useAtomSetter(atoms.settings.perfMode);
const perfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const isPerfModeToggleable = useAtomGetter(atoms.settings.isPerfModeToggleable);
const languages = useAtomGetter(atoms.localData.languages);
const { format } = useFormat();
@ -237,6 +241,20 @@ export const SettingsPopup = (): JSX.Element => {
}}
/>
</div>
<div className="grid place-items-center">
<div className="flex place-content-center place-items-center gap-1">
<h3 className="text-xl">{format("performance_mode")}</h3>
<ToolTip content={format("performance_mode_tooltip")} placement="top">
<Ico icon="info" />
</ToolTip>
</div>
<Switch
value={perfModeEnabled}
onClick={() => setPerfMode(perfModeEnabled ? PerfMode.Off : PerfMode.On)}
disabled={!isPerfModeToggleable}
/>
</div>
</div>
</div>
</Popup>

View File

@ -77,6 +77,7 @@ export const PreviewCard = ({
disabled = false,
onClick,
}: Props): JSX.Element => {
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const currency = useAtomGetter(atoms.settings.currency);
const currencies = useAtomGetter(atoms.localData.currencies);
const isHoverable = useDeviceSupportsHover();
@ -117,7 +118,7 @@ export const PreviewCard = ({
return (
<UpPressable
className={cJoin("grid items-end text-left", className)}
className={cJoin("relative grid items-end text-left", className)}
href={href}
onClick={onClick}
noBackground
@ -177,11 +178,11 @@ export const PreviewCard = ({
"z-20 grid gap-2 p-4 transition-opacity linearbg-obi",
cIf(
!keepInfoVisible && isHoverable,
`-inset-x-0.5 bottom-2 opacity-0 shadow-shade
`-inset-x-0.5 bottom-2 opacity-0 !shadow-shade
[border-radius:10%_10%_10%_10%_/_1%_1%_3%_3%]
group-hover:opacity-100 hoverable:absolute hoverable:drop-shadow-lg
group-hover:opacity-100 hoverable:absolute hoverable:shadow-lg
notHoverable:rounded-b-md notHoverable:opacity-100`,
"[border-radius:0%_0%_10%_10%_/_0%_0%_3%_3%]"
cIf(!isPerfModeEnabled, "[border-radius:0%_0%_10%_10%_/_0%_0%_3%_3%]")
)
)}>
{metadata?.position === "Top" && metadataJSX}

View File

@ -1,6 +1,7 @@
import { atom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { containerQueries } from "contexts/containerQueries";
import { userAgent } from "contexts/userAgent";
import { atomPairing } from "helpers/atoms";
import { settings } from "contexts/settings";
import { UploadImageFragment } from "graphql/generated";
@ -40,6 +41,8 @@ const searchOpened = atomPairing(atom(false));
const settingsOpened = atomPairing(atom(false));
const subPanelOpened = atomPairing(atom(false));
const mainPanelOpened = atomPairing(atom(false));
const debugMenuOpened = atomPairing(atom(false));
const debugMenuAvailable = atom((get) => get(settings.playerName[0]) === "debug");
const menuGesturesEnabled = atomPairing(atomWithStorage("isMenuGesturesEnabled", false));
const terminalMode = atom((get) => get(settings.playerName[0]) === "root");
@ -51,6 +54,8 @@ const layout = {
mainPanelOpened,
menuGesturesEnabled,
terminalMode,
debugMenuAvailable,
debugMenuOpened,
};
export const atoms = {
@ -59,6 +64,7 @@ export const atoms = {
localData,
lightBox,
containerQueries,
userAgent,
};
// Do not import outside of the "contexts" folder

View File

@ -6,6 +6,7 @@ import { atomPairing, useAtomGetter, useAtomPair } from "helpers/atoms";
import { getDefaultPreferredLanguages } from "helpers/locales";
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
import { usePrefersDarkMode } from "hooks/useMediaQuery";
import { userAgent } from "contexts/userAgent";
export enum ThemeMode {
Dark = "dark",
@ -13,13 +14,43 @@ export enum ThemeMode {
Light = "light",
}
export enum PerfMode {
On = "on",
Auto = "auto",
Off = "off",
}
const preferredLanguagesAtom = atomPairing(atomWithStorage<string[]>("preferredLanguages", []));
const themeModeAtom = atomPairing(atomWithStorage<ThemeMode>("themeMode", ThemeMode.Auto));
const themeModeAtom = atomPairing(atomWithStorage("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", ""));
const perfModeAtom = atomPairing(atomWithStorage("perfMode", PerfMode.Auto));
const isPerfModeEnabledAtom = atom((get) => {
const os = get(userAgent.os);
const engine = get(userAgent.engine);
const perfMode = get(perfModeAtom[0]);
if (os === "iOS") return true;
if (engine === "WebKit") return true;
if (perfMode === "auto") {
if (engine === "Blink") return false;
if (os === "Linux") return true;
if (os === "Android") return true;
}
return perfMode === PerfMode.On;
});
const isPerfModeToggleableAtom = atom((get) => {
const engine = get(userAgent.engine);
const os = get(userAgent.os);
if (os === "iOS") return false;
if (engine === "WebKit") return false;
return true;
});
export const settings = {
preferredLanguages: preferredLanguagesAtom,
@ -29,6 +60,9 @@ export const settings = {
dyslexic: dyslexicAtom,
currency: currencyAtom,
playerName: playerNameAtom,
perfMode: perfModeAtom,
isPerfModeEnabled: isPerfModeEnabledAtom,
isPerfModeToggleable: isPerfModeToggleableAtom,
};
export const useSettings = (): void => {

45
src/contexts/userAgent.ts Normal file
View File

@ -0,0 +1,45 @@
import { atom } from "jotai";
import { useIsClient } from "usehooks-ts";
import { useEffect } from "react";
import { UAParser } from "ua-parser-js";
import { atomPairing, useAtomSetter } from "helpers/atoms";
import { getLogger } from "helpers/logger";
const logger = getLogger("📱 [User Agent]");
const osAtom = atomPairing(atom<string | undefined>(undefined));
const browserAtom = atomPairing(atom<string | undefined>(undefined));
const engineAtom = atomPairing(atom<string | undefined>(undefined));
const deviceTypeAtom = atomPairing(atom<string | undefined>(undefined));
export const userAgent = {
os: osAtom[0],
browser: browserAtom[0],
engine: engineAtom[0],
deviceType: deviceTypeAtom[0],
};
export const useUserAgent = (): void => {
const setOs = useAtomSetter(osAtom);
const setBrowser = useAtomSetter(browserAtom);
const setEngine = useAtomSetter(engineAtom);
const setDeviceType = useAtomSetter(deviceTypeAtom);
const isClient = useIsClient();
useEffect(() => {
const parser = new UAParser();
const os = parser.getOS().name;
const browser = parser.getBrowser().name;
const engine = parser.getEngine().name;
const deviceType = parser.getDevice().type;
setOs(os);
setBrowser(browser);
setEngine(engine);
setDeviceType(deviceType);
logger.log({ os, browser, engine, deviceType });
}, [isClient, setBrowser, setDeviceType, setEngine, setOs]);
};

View File

@ -1,18 +0,0 @@
import { useEffect } from "react";
import { isDefined } from "helpers/asserts";
import { useIsWebkit } from "hooks/useIsWebkit";
export const useWebkitFixes = (): void => {
const isWebkit = useIsWebkit();
useEffect(() => {
const next = document.getElementById("__next");
if (isDefined(next)) {
if (isWebkit) {
next.classList.add("webkit-fixes");
} else {
next.classList.remove("webkit-fixes");
}
}
}, [isWebkit]);
};

View File

@ -186,4 +186,6 @@ export interface ICUParams {
player_name_tooltip: never;
download_scans: never;
search_placeholder: never;
performance_mode: never;
performance_mode_tooltip: never;
}

View File

@ -193,6 +193,8 @@ query localDataGetWebsiteInterfaces {
player_name_tooltip
download_scans
search_placeholder
performance_mode
performance_mode_tooltip
}
}
}

View File

@ -1,14 +0,0 @@
import { useMemo } from "react";
import UAParser from "ua-parser-js";
import { useIsClient } from "usehooks-ts";
export const useIsWebkit = (): boolean => {
const isClient = useIsClient();
return useMemo<boolean>(() => {
if (isClient) {
const parser = new UAParser();
return parser.getBrowser().name === "Safari" || parser.getOS().name === "iOS";
}
return false;
}, [isClient]);
};

View File

@ -20,22 +20,24 @@ import { LightBoxProvider } from "contexts/LightBoxProvider";
import { SettingsPopup } from "components/Panels/SettingsPopup";
import { useSettings } from "contexts/settings";
import { useContainerQueries } from "contexts/containerQueries";
import { useWebkitFixes } from "contexts/webkitFixes";
import { SearchPopup } from "components/Panels/SearchPopup";
import { useScrollIntoView } from "hooks/useScrollIntoView";
import { useUserAgent } from "contexts/userAgent";
import { DebugPopup } from "components/Panels/DebugPopup";
const AccordsLibraryApp = (props: AppProps): JSX.Element => {
useLocalData();
useSettings();
useContainerQueries();
useWebkitFixes();
useScrollIntoView();
useUserAgent();
return (
<>
<SearchPopup />
<SettingsPopup />
<LightBoxProvider />
<DebugPopup />
<Script
data-website-id={process.env.NEXT_PUBLIC_UMAMI_ID}
src={`${process.env.NEXT_PUBLIC_UMAMI_URL}/script.js`}

View File

@ -10,6 +10,7 @@ import { sendAnalytics } from "helpers/analytics";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { useFormat } from "hooks/useFormat";
import { Button } from "components/Inputs/Button";
/*
*
@ -146,10 +147,10 @@ const AboutUs = (props: PostStaticProps): JSX.Element => {
/>
</div>
<input
<Button
type="submit"
value={format("send")}
className="w-min !px-6"
text={format("send")}
disabled={formState !== "stale"}
/>
</div>

View File

@ -73,6 +73,7 @@ interface Props extends AppLayoutRequired {
const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
const currency = useAtomGetter(atoms.settings.currency);
const isPerfModeEnabled = useAtomGetter(atoms.settings.isPerfModeEnabled);
const { format, formatLibraryItemType } = useFormat();
const currencies = useAtomGetter(atoms.localData.currencies);
@ -178,7 +179,8 @@ const LibrarySlug = ({ item, itemId, ...otherProps }: Props): JSX.Element => {
<div className="grid place-items-center gap-12">
<div
className={cJoin(
"relative h-[50vh] w-full cursor-pointer drop-shadow-xl shadow-shade",
"relative h-[50vh] w-full cursor-pointer",
cIf(!isPerfModeEnabled, "drop-shadow-xl shadow-shade"),
cIf(isContentPanelAtLeast3xl, "mb-16", "h-[60vh]")
)}>
{item.thumbnail?.data?.attributes ? (

View File

@ -4,6 +4,7 @@ import { useHotkeys } from "react-hotkeys-hook";
import Slider from "rc-slider";
import { TransformWrapper, TransformComponent } from "react-zoom-pan-pinch";
import { z } from "zod";
import { atom } from "jotai";
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
import {
Enum_Componentmetadatabooks_Page_Order as PageOrder,
@ -37,7 +38,6 @@ import { useFullscreen } from "hooks/useFullscreen";
import { atoms } from "contexts/atoms";
import { useAtomGetter } from "helpers/atoms";
import { FilterSettings, useReaderSettings } from "hooks/useReaderSettings";
import { useIsWebkit } from "hooks/useIsWebkit";
import { useTypedRouter } from "hooks/useTypedRouter";
import { useFormat } from "hooks/useFormat";
import { getFormat } from "helpers/i18n";
@ -71,6 +71,8 @@ const queryParamSchema = z.object({
page: z.coerce.number().optional(),
});
const isWebKitAtom = atom((get) => get(atoms.userAgent.engine) === "WebKit");
/*
*
* PAGE
@ -120,7 +122,7 @@ const LibrarySlug = ({
is1ColumnLayout ? "single" : "double"
);
const router = useTypedRouter(queryParamSchema);
const isWebkit = useIsWebkit();
const isWebKit = useAtomGetter(isWebKitAtom);
const { isFullscreen, toggleFullscreen, requestFullscreen } = useFullscreen(Ids.ContentPanel);
@ -299,7 +301,7 @@ const LibrarySlug = ({
<Switch value={isSidePagesEnabled} onClick={toggleIsSidePagesEnabled} />
</WithLabel>
{!isWebkit && (
{!isWebKit && (
<WithLabel label={format("shadow")}>
<Switch value={filterSettings.dropShadow} onClick={toggleDropShadow} />
</WithLabel>
@ -394,7 +396,7 @@ const LibrarySlug = ({
display: "grid",
placeContent: "center",
filter:
!filterSettings.dropShadow || isWebkit
!filterSettings.dropShadow || isWebKit
? undefined
: isDarkMode
? CUSTOM_DARK_DROPSHADOW

View File

@ -54,13 +54,6 @@ textarea {
@apply rounded-2xl p-6 text-left;
}
input[type="submit"] {
@apply grid cursor-pointer place-content-center place-items-center rounded-full border
border-dark px-4 pb-[0.5rem] pt-[0.4rem] text-dark outline-none transition-all shadow-shade
hover:bg-dark hover:text-light hover:drop-shadow-lg active:border-black active:bg-black
active:text-light active:drop-shadow-lg active:shadow-black;
}
input:enabled,
textarea:enabled {
@apply hover:bg-mid hover:outline-transparent;

View File

@ -250,21 +250,6 @@ export default {
});
}),
/* Webkit fixes */
plugin(({ addUtilities }) => {
addUtilities({
".webkit-fixes": {
"*": {
"--tw-drop-shadow": "unset !important",
"--tw-shadow": "unset !important",
},
".texture-paper-dots": {
backgroundImage: "unset !important",
},
},
});
}),
/* Add support for break-wrods CSS attribute */
plugin(({ addUtilities }) => {
addUtilities({