Moved assert helpers into their own folder + activated some tsconfig strict options
This commit is contained in:
parent
e9950602c4
commit
0ddd46643b
|
@ -3,7 +3,7 @@ import { useSwipeable } from "react-swipeable";
|
|||
import { layout } from "../../design.config";
|
||||
import { Ico, Icon } from "./Ico";
|
||||
import { MainPanel } from "./Panels/MainPanel";
|
||||
import { isDefined, isUndefined } from "helpers/others";
|
||||
import { isDefined, isUndefined } from "helpers/asserts";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { OpenGraph, TITLE_PREFIX, TITLE_SEPARATOR } from "helpers/openGraph";
|
||||
import { Ids } from "types/ids";
|
||||
|
@ -239,7 +239,7 @@ const ContentPlaceholder = ({ message, icon }: ContentPlaceholderProps): JSX.Ele
|
|||
className="grid grid-flow-col place-items-center gap-9 rounded-2xl border-2 border-dotted
|
||||
border-dark p-8 text-dark opacity-40">
|
||||
{isDefined(icon) && <Ico icon={icon} className="!text-[300%]" />}
|
||||
<p className={cJoin("w-64 text-2xl", cIf(!isDefined(icon), "text-center"))}>{message}</p>
|
||||
<p className={cJoin("w-64 text-2xl", cIf(isUndefined(icon), "text-center"))}>{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,7 @@ import { DatePickerFragment } from "graphql/generated";
|
|||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { DownPressable } from "components/Containers/DownPressable";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
/*
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback } from "react";
|
|||
import { useBoolean } from "usehooks-ts";
|
||||
import { TranslatedChroniclePreview } from "./ChroniclePreview";
|
||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { prettyInlineTitle, prettySlug, sJoin } from "helpers/formatters";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { compareDate } from "helpers/date";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useRouter } from "next/router";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomSetter, useAtomPair } from "helpers/atoms";
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { MouseEventHandler, useCallback } from "react";
|
|||
import { Link } from "./Link";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Button } from "./Button";
|
|||
import { ToolTip } from "components/ToolTip";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import React, { MouseEventHandler } from "react";
|
||||
import NextLink from "next/link";
|
||||
import { ConditionalWrapper, Wrapper } from "helpers/component";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Fragment, useCallback } from "react";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
import { arrayMove, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { arrayMove } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
@ -16,6 +17,14 @@ interface Props {
|
|||
|
||||
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
|
||||
|
||||
interface InsertedLabelProps {
|
||||
label?: string;
|
||||
}
|
||||
|
||||
const InsertedLabel = ({ label }: InsertedLabelProps) => (
|
||||
<>{isDefinedAndNotEmpty(label) && <p>{label}</p>}</>
|
||||
);
|
||||
|
||||
export const OrderableList = ({ onChange, items, insertLabels }: Props): JSX.Element => {
|
||||
const updateOrder = useCallback(
|
||||
(sourceIndex: number, targetIndex: number) => {
|
||||
|
@ -29,9 +38,8 @@ export const OrderableList = ({ onChange, items, insertLabels }: Props): JSX.Ele
|
|||
<div className="grid gap-2">
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={index}>
|
||||
{insertLabels && isDefinedAndNotEmpty(insertLabels[index]?.name) && (
|
||||
<p>{insertLabels[index].name}</p>
|
||||
)}
|
||||
<InsertedLabel label={insertLabels?.[index]?.name} />
|
||||
|
||||
<div
|
||||
onDragStart={(event) => {
|
||||
const source = event.target as HTMLElement;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
|
|
@ -9,7 +9,7 @@ import { useFullscreen } from "hooks/useFullscreen";
|
|||
import { Ids } from "types/ids";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { InsetBox } from "components/Containers/InsetBox";
|
|||
import { cIf, cJoin } from "helpers/className";
|
||||
import { slugify } from "helpers/formatters";
|
||||
import { getAssetURL, ImageQuality } from "helpers/img";
|
||||
import { isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/others";
|
||||
import { isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/asserts";
|
||||
import { AnchorShare } from "components/AnchorShare";
|
||||
import { useIntersectionList } from "hooks/useIntersectionList";
|
||||
import { Ico, Icon } from "components/Ico";
|
||||
|
@ -459,7 +459,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
|||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h2 >= 0 && line.startsWith('<Header level="3"')) {
|
||||
toc.children[h2].children.push({
|
||||
toc.children[h2]?.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
|
@ -469,7 +469,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
|||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h3 >= 0 && line.startsWith('<Header level="4"')) {
|
||||
toc.children[h2].children[h3].children.push({
|
||||
toc.children[h2]?.children[h3]?.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
|
@ -478,7 +478,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
|||
h5 = -1;
|
||||
scenebreak = 0;
|
||||
} else if (h4 >= 0 && line.startsWith('<Header level="5"')) {
|
||||
toc.children[h2].children[h3].children[h4].children.push({
|
||||
toc.children[h2]?.children[h3]?.children[h4]?.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
|
@ -486,7 +486,7 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
|||
h5++;
|
||||
scenebreak = 0;
|
||||
} else if (h5 >= 0 && line.startsWith('<Header level="6"')) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push({
|
||||
toc.children[h2]?.children[h3]?.children[h4]?.children[h5]?.children.push({
|
||||
title: getTitle(line),
|
||||
slug: getSlug(line),
|
||||
children: [],
|
||||
|
@ -502,13 +502,13 @@ const getTocFromMarkdawn = (text: string, title?: string): TocInterface => {
|
|||
};
|
||||
|
||||
if (h5 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children[h5].children.push(child);
|
||||
toc.children[h2]?.children[h3]?.children[h4]?.children[h5]?.children.push(child);
|
||||
} else if (h4 >= 0) {
|
||||
toc.children[h2].children[h3].children[h4].children.push(child);
|
||||
toc.children[h2]?.children[h3]?.children[h4]?.children.push(child);
|
||||
} else if (h3 >= 0) {
|
||||
toc.children[h2].children[h3].children.push(child);
|
||||
toc.children[h2]?.children[h3]?.children.push(child);
|
||||
} else if (h2 >= 0) {
|
||||
toc.children[h2].children.push(child);
|
||||
toc.children[h2]?.children.push(child);
|
||||
} else {
|
||||
toc.children.push(child);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { MouseEventHandler, useCallback } from "react";
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { DownPressable } from "components/Containers/DownPressable";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { Ico, Icon } from "components/Ico";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
|
|
@ -3,7 +3,7 @@ import { Icon } from "components/Ico";
|
|||
import { Button } from "components/Inputs/Button";
|
||||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isUndefined } from "helpers/asserts";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
|
@ -30,7 +30,7 @@ export const ReturnButton = ({ href, title, displayOnlyOn, className }: Props):
|
|||
<>
|
||||
{((is3ColumnsLayout && displayOnlyOn === "3ColumnsLayout") ||
|
||||
(!is3ColumnsLayout && displayOnlyOn === "1ColumnLayout") ||
|
||||
!isDefined(displayOnlyOn)) && (
|
||||
isUndefined(displayOnlyOn)) && (
|
||||
<div className={className}>
|
||||
<Button href={href} text={`${langui.return_to} ${title}`} icon={Icon.NavigateBefore} />
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ import { NavOption } from "components/PanelComponents/NavOption";
|
|||
import { ToolTip } from "components/ToolTip";
|
||||
import { Icon } from "components/Ico";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { Link } from "components/Inputs/Link";
|
||||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { ColoredSvg } from "components/ColoredSvg";
|
||||
|
|
|
@ -10,7 +10,7 @@ import { Popup } from "components/Containers/Popup";
|
|||
import { sendAnalytics } from "helpers/analytics";
|
||||
import { cJoin, cIf } from "helpers/className";
|
||||
import { prettyLanguage } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter, useAtomPair } from "helpers/atoms";
|
||||
import { ThemeMode } from "contexts/settings";
|
||||
|
@ -39,15 +39,10 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
);
|
||||
|
||||
const [currencySelect, setCurrencySelect] = useState<number>(-1);
|
||||
|
||||
useEffect(() => {
|
||||
if (isDefined(currency)) setCurrencySelect(currencyOptions.indexOf(currency));
|
||||
}, [currency, currencyOptions]);
|
||||
|
||||
useEffect(() => {
|
||||
if (currencySelect >= 0) setCurrency(currencyOptions[currencySelect]);
|
||||
}, [currencyOptions, currencySelect, setCurrency]);
|
||||
|
||||
return (
|
||||
<Popup
|
||||
isVisible={isSettingsOpened}
|
||||
|
@ -134,8 +129,11 @@ export const SettingsPopup = (): JSX.Element => {
|
|||
options={currencyOptions}
|
||||
value={currencySelect}
|
||||
onChange={(newCurrency) => {
|
||||
setCurrencySelect(newCurrency);
|
||||
sendAnalytics("Settings", `Change currency (${currencyOptions[newCurrency]})}`);
|
||||
const newCurrencyName = currencyOptions[newCurrency];
|
||||
if (isDefined(newCurrencyName)) {
|
||||
setCurrency(newCurrencyName);
|
||||
sendAnalytics("Settings", `Change currency (${currencyOptions[newCurrency]})}`);
|
||||
}
|
||||
}}
|
||||
className="w-28"
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,8 @@ import { ThumbnailHeader } from "./ThumbnailHeader";
|
|||
import { ToolTip } from "./ToolTip";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { PostWithTranslations } from "types/types";
|
||||
import { filterHasAttributes, getStatusDescription } from "helpers/others";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
|
|
@ -7,7 +7,7 @@ import { ImageQuality } from "helpers/img";
|
|||
import { TranslatedProps } from "types/TranslatedProps";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { cIf, cJoin } from "helpers/className";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
|
||||
/*
|
||||
* ╭─────────────╮
|
||||
|
|
|
@ -5,7 +5,7 @@ import { ToolTip } from "./ToolTip";
|
|||
import { Chip } from "components/Chip";
|
||||
import { RecorderChipFragment } from "graphql/generated";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Chip } from "./Chip";
|
|||
import { PageSelector } from "./Inputs/PageSelector";
|
||||
import { Ico, Icon } from "./Ico";
|
||||
import { cJoin } from "helpers/className";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { Ids } from "types/ids";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
@ -95,17 +95,17 @@ export const SmartList = <T,>({
|
|||
const memo: Group<T>[] = [];
|
||||
|
||||
sortedItem.forEach((item) => {
|
||||
groupingFunction(item).forEach((category) => {
|
||||
const index = memo.findIndex((group) => group.name === category);
|
||||
if (index === -1) {
|
||||
groupingFunction(item).forEach((groupName) => {
|
||||
const group = memo.find((elem) => elem.name === groupName);
|
||||
if (isDefined(group)) {
|
||||
group.items.push(item);
|
||||
group.totalCount += groupCountingFunction(item);
|
||||
} else {
|
||||
memo.push({
|
||||
name: category,
|
||||
name: groupName,
|
||||
items: [item],
|
||||
totalCount: groupCountingFunction(item),
|
||||
});
|
||||
} else {
|
||||
memo[index].items.push(item);
|
||||
memo[index].totalCount += groupCountingFunction(item);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -170,7 +170,7 @@ export const SmartList = <T,>({
|
|||
)}
|
||||
|
||||
<div className="mb-8">
|
||||
{pages[page]?.length > 0 ? (
|
||||
{(pages[page]?.length ?? 0) > 0 ? (
|
||||
pages[page]?.map(
|
||||
(group) =>
|
||||
group.items.length > 0 && (
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Markdawn } from "components/Markdown/Markdawn";
|
|||
import { GetContentTextQuery, UploadImageFragment } from "graphql/generated";
|
||||
import { prettyInlineTitle, prettySlug, slugify } from "helpers/formatters";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
import { atoms } from "contexts/atoms";
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@ 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 { filterDefined } from "helpers/asserts";
|
||||
import { atomPairing, useAtomSetter } from "helpers/atoms";
|
||||
|
||||
const lightBoxAtom = atomPairing(
|
||||
|
|
|
@ -4,7 +4,7 @@ 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 { isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { usePrefersDarkMode } from "hooks/useMediaQuery";
|
||||
|
||||
export enum ThemeMode {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useLayoutEffect } from "react";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
import { useIsWebkit } from "hooks/useIsWebkit";
|
||||
|
||||
export const useWebkitFixes = (): void => {
|
||||
|
|
|
@ -5,7 +5,7 @@ import { PostWithTranslations } from "types/types";
|
|||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { prettyDate, prettySlug } from "helpers/formatters";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { getDescription } from "helpers/description";
|
||||
import { AppLayoutRequired } from "components/AppLayout";
|
||||
|
||||
|
@ -25,7 +25,7 @@ export const getPostStaticProps =
|
|||
if (
|
||||
post.posts?.data &&
|
||||
post.posts.data.length > 0 &&
|
||||
post.posts.data[0].attributes?.translations &&
|
||||
post.posts.data[0]?.attributes?.translations &&
|
||||
isDefined(context.locale) &&
|
||||
isDefined(context.locales)
|
||||
) {
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
type JoinDot<K extends string, P extends string> = `${K}${"" extends K ? "" : "."}${P}`;
|
||||
|
||||
type PathDot<T, Acc extends string = ""> = T extends object
|
||||
? {
|
||||
[K in keyof T]: K extends string ? JoinDot<Acc, K> | PathDot<T[K], JoinDot<Acc, K>> : never;
|
||||
}[keyof T]
|
||||
: Acc;
|
||||
|
||||
type PathHead<T extends unknown[]> = T extends [infer head]
|
||||
? head
|
||||
: // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
T extends [infer head, ...infer rest]
|
||||
? head
|
||||
: "";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
type PathRest<T extends unknown[]> = T extends [infer head, ...infer rest]
|
||||
? rest extends []
|
||||
? never
|
||||
: rest
|
||||
: never;
|
||||
|
||||
type PathLast<T extends unknown[]> = T["length"] extends 1 ? true : false;
|
||||
|
||||
type Recursive<T, Path extends unknown[]> = PathHead<Path> extends keyof T
|
||||
? Omit<T, PathHead<Path>> & {
|
||||
[P in PathHead<Path>]-?: PathLast<Path> extends true
|
||||
? NonNullable<T[P]>
|
||||
: Recursive<NonNullable<T[P]>, PathRest<Path>>;
|
||||
}
|
||||
: T;
|
||||
|
||||
type Split<Str, Acc extends string[] = []> = Str extends `${infer Head}.${infer Rest}`
|
||||
? Split<Rest, [...Acc, Head]>
|
||||
: Str extends `${infer Last}`
|
||||
? [...Acc, Last]
|
||||
: never;
|
||||
|
||||
export type SelectiveNonNullable<T, P extends PathDot<T>> = Recursive<NonNullable<T>, Split<P>>;
|
||||
|
||||
export const isDefined = <T>(t: T): t is NonNullable<T> => t !== null && t !== undefined;
|
||||
|
||||
export const isUndefined = <T>(t: T | null | undefined): t is null | undefined => !isDefined(t);
|
||||
|
||||
export const isDefinedAndNotEmpty = (string: string | null | undefined): string is string =>
|
||||
isDefined(string) && string.length > 0;
|
||||
|
||||
export const filterDefined = <T>(t: T[] | null | undefined): NonNullable<T>[] =>
|
||||
isUndefined(t) ? [] : (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
||||
|
||||
export const filterHasAttributes = <T, P extends PathDot<T>>(
|
||||
t: T[] | null | undefined,
|
||||
paths: readonly P[]
|
||||
): SelectiveNonNullable<T, typeof paths[number]>[] =>
|
||||
isDefined(t)
|
||||
? (t.filter((item) => hasAttributes(item, paths)) as unknown as SelectiveNonNullable<
|
||||
T,
|
||||
typeof paths[number]
|
||||
>[])
|
||||
: [];
|
||||
|
||||
const hasAttributes = <T>(item: T, paths: readonly PathDot<T>[]): boolean =>
|
||||
isDefined(item) && paths.every((path) => hasAttribute(item, path));
|
||||
|
||||
const hasAttribute = <T>(item: T, path: string): boolean => {
|
||||
if (isDefined(item)) {
|
||||
const [head, ...rest] = path.split(".");
|
||||
if (isDefined(head) && Object.keys(item).includes(head)) {
|
||||
const attribute = head as keyof T;
|
||||
if (isDefined(item[attribute])) {
|
||||
if (rest.length > 0) {
|
||||
return hasAttribute(item[attribute], rest.join("."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
|
@ -1,4 +1,4 @@
|
|||
import { isDefined } from "./others";
|
||||
import { isDefined } from "./asserts";
|
||||
|
||||
export interface Wrapper {
|
||||
children: React.ReactNode;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isUndefined } from "./others";
|
||||
import { isUndefined } from "./asserts";
|
||||
import { DatePickerFragment } from "graphql/generated";
|
||||
|
||||
export const compareDate = (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { filterDefined, isDefined, isDefinedAndNotEmpty } from "./others";
|
||||
import { filterDefined, isDefined, isDefinedAndNotEmpty } from "./asserts";
|
||||
|
||||
export const getDescription = (
|
||||
description: string | null | undefined,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { convertPrice } from "./numbers";
|
||||
import { isDefinedAndNotEmpty, isUndefined } from "./others";
|
||||
import { isDefinedAndNotEmpty, isUndefined } from "./asserts";
|
||||
import { datePickerToDate } from "./date";
|
||||
import { Currencies, Languages, Langui } from "./localData";
|
||||
import { DatePickerFragment, PricePickerFragment } from "graphql/generated";
|
||||
|
@ -180,7 +180,7 @@ export const prettyItemSubType = (
|
|||
case "ComponentMetadataGame":
|
||||
return metadata.platforms?.data &&
|
||||
metadata.platforms.data.length > 0 &&
|
||||
metadata.platforms.data[0].attributes
|
||||
metadata.platforms.data[0]?.attributes
|
||||
? metadata.platforms.data[0].attributes.short
|
||||
: "";
|
||||
case "ComponentMetadataGroup": {
|
||||
|
|
|
@ -27,13 +27,11 @@ const imageQualityProperties: Record<ImageQuality, ImageProperties> = {
|
|||
};
|
||||
|
||||
export const getAssetFilename = (path: string): string => {
|
||||
let result = path.split("/");
|
||||
result = result[result.length - 1].split(".");
|
||||
result = result
|
||||
.splice(0, result.length - 1)
|
||||
.join(".")
|
||||
.split("_");
|
||||
return result[0];
|
||||
// /uploads/329_7f41d09a98.webp -> 329_7f41d09a98.webp
|
||||
let result = path.substring(path.lastIndexOf("/") + 1);
|
||||
// 329_7f41d09a98.webp -> 329
|
||||
result = result.substring(0, result.indexOf("_"));
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getAssetURL = (url: string, quality: ImageQuality): string => {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isDefined } from "./others";
|
||||
import { isDefined } from "./asserts";
|
||||
|
||||
export const isUntangibleGroupItem = (
|
||||
metadata:
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isDefined } from "./others";
|
||||
import { isDefined } from "./asserts";
|
||||
|
||||
export const getDefaultPreferredLanguages = (routerLocal: string, locales: string[]): string[] => {
|
||||
let defaultPreferredLanguages: string[] = [];
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { OgImage, getImgSizesByQuality, ImageQuality, getAssetURL } from "./img";
|
||||
import { isDefinedAndNotEmpty } from "./others";
|
||||
import { isDefinedAndNotEmpty } from "./asserts";
|
||||
import { Langui } from "./localData";
|
||||
import { UploadImageFragment } from "graphql/generated";
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { PathDot, SelectiveNonNullable } from "../types/SelectiveNonNullable";
|
||||
import { Langui } from "./localData";
|
||||
import { isDefined } from "./asserts";
|
||||
import {
|
||||
Enum_Componentsetstextset_Status,
|
||||
GetLibraryItemQuery,
|
||||
|
@ -45,47 +45,6 @@ export const getStatusDescription = (status: string, langui: Langui): string | n
|
|||
}
|
||||
};
|
||||
|
||||
export const isDefined = <T>(t: T): t is NonNullable<T> => t !== null && t !== undefined;
|
||||
|
||||
export const isUndefined = <T>(t: T | null | undefined): t is null | undefined =>
|
||||
t === null || t === undefined;
|
||||
|
||||
export const isDefinedAndNotEmpty = (string: string | null | undefined): string is string =>
|
||||
isDefined(string) && string.length > 0;
|
||||
|
||||
export const filterDefined = <T>(t: T[] | null | undefined): NonNullable<T>[] =>
|
||||
isUndefined(t) ? [] : (t.filter((item) => isDefined(item)) as NonNullable<T>[]);
|
||||
|
||||
export const filterHasAttributes = <T, P extends PathDot<T>>(
|
||||
t: T[] | null | undefined,
|
||||
paths: readonly P[]
|
||||
): SelectiveNonNullable<T, typeof paths[number]>[] =>
|
||||
isDefined(t)
|
||||
? (t.filter((item) => hasAttributes(item, paths)) as unknown as SelectiveNonNullable<
|
||||
T,
|
||||
typeof paths[number]
|
||||
>[])
|
||||
: [];
|
||||
|
||||
const hasAttributes = <T>(item: T, paths: readonly PathDot<T>[]): boolean =>
|
||||
isDefined(item) && paths.every((path) => hasAttribute(item, path));
|
||||
|
||||
const hasAttribute = <T>(item: T, path: string): boolean => {
|
||||
if (isDefined(item)) {
|
||||
const [head, ...rest] = path.split(".");
|
||||
if (Object.keys(item).includes(head)) {
|
||||
const attribute = head as keyof T;
|
||||
if (isDefined(item[attribute])) {
|
||||
if (rest.length > 0) {
|
||||
return hasAttribute(item[attribute], rest.join("."));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
export const iterateMap = <K, V, U>(
|
||||
map: Map<K, V>,
|
||||
callbackfn: (key: K, value: V, index: number) => U,
|
||||
|
@ -99,7 +58,10 @@ export const iterateMap = <K, V, U>(
|
|||
};
|
||||
|
||||
export const arrayMove = <T>(arr: T[], sourceIndex: number, targetIndex: number): T[] => {
|
||||
arr.splice(targetIndex, 0, arr.splice(sourceIndex, 1)[0]);
|
||||
const sourceItem = arr.splice(sourceIndex, 1)[0];
|
||||
if (isDefined(sourceItem)) {
|
||||
arr.splice(targetIndex, 0, sourceItem);
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { isDefinedAndNotEmpty } from "./others";
|
||||
import { isDefinedAndNotEmpty } from "./asserts";
|
||||
|
||||
export const prettyTerminalUnderlinedTitle = (string: string | null | undefined): string =>
|
||||
isDefinedAndNotEmpty(string)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useIsClient } from "usehooks-ts";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
|
||||
export const useFullscreen = (
|
||||
id: string
|
||||
|
|
|
@ -2,7 +2,7 @@ import { useCallback, useEffect, useState } from "react";
|
|||
import { throttle } from "throttle-debounce";
|
||||
import { useIsClient } from "usehooks-ts";
|
||||
import { useOnScroll } from "./useOnScroll";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined, isUndefined } from "helpers/asserts";
|
||||
import { Ids } from "types/ids";
|
||||
|
||||
export const useIntersectionList = (ids: string[]): number => {
|
||||
|
@ -16,21 +16,21 @@ export const useIntersectionList = (ids: string[]): number => {
|
|||
(scroll: number) => {
|
||||
console.log("useIntersectionList");
|
||||
|
||||
if (!isDefined(contentPanel)) {
|
||||
if (isUndefined(contentPanel)) {
|
||||
setCurrentIntersection(-1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (let idIndex = 0; idIndex < ids.length; idIndex++) {
|
||||
const elem = document.getElementById(ids[ids.length - 1 - idIndex]);
|
||||
for (const [index, id] of [...ids].reverse().entries()) {
|
||||
const elem = document.getElementById(id);
|
||||
const halfScreenOffset = window.screen.height / 2;
|
||||
|
||||
if (isDefined(elem) && scroll > elem.offsetTop - halfScreenOffset) {
|
||||
setCurrentIntersection(ids.length - 1 - idIndex);
|
||||
const unreversedIndex = ids.length - 1 - index;
|
||||
setCurrentIntersection(unreversedIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
setCurrentIntersection(-1);
|
||||
},
|
||||
[ids, contentPanel]
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { useEffect } from "react";
|
||||
import { useIsClient } from "usehooks-ts";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
|
||||
export const useOnResize = (
|
||||
id: string,
|
||||
|
@ -11,9 +11,12 @@ export const useOnResize = (
|
|||
useEffect(() => {
|
||||
console.log("[useOnResize]", id);
|
||||
const elem = isClient ? document.querySelector(`#${id}`) : null;
|
||||
const ro = new ResizeObserver((resizeObserverEntry) =>
|
||||
onResize(resizeObserverEntry[0].contentRect.width, resizeObserverEntry[0].contentRect.height)
|
||||
);
|
||||
const ro = new ResizeObserver((resizeObserverEntry) => {
|
||||
const entry = resizeObserverEntry[0];
|
||||
if (isDefined(entry)) {
|
||||
onResize(entry.contentRect.width, entry.contentRect.height);
|
||||
}
|
||||
});
|
||||
if (isDefined(elem)) {
|
||||
ro.observe(elem);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useCounter } from "usehooks-ts";
|
||||
import { isDefined } from "helpers/others";
|
||||
import { isDefined } from "helpers/asserts";
|
||||
|
||||
const NUM_RETRIES = 10;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { useRouter } from "next/router";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { LanguageSwitcher } from "components/Inputs/LanguageSwitcher";
|
||||
import { filterDefined, isDefined } from "helpers/others";
|
||||
import { filterDefined, isDefined } from "helpers/asserts";
|
||||
import { getPreferredLanguage } from "helpers/locales";
|
||||
import { atoms } from "contexts/atoms";
|
||||
import { useAtomGetter } from "helpers/atoms";
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import { i18n } from "../../../next.config";
|
||||
import { cartesianProduct, filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { cartesianProduct } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { fetchLocalData } from "graphql/fetchLocalData";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
|
||||
|
@ -183,7 +184,7 @@ const Revalidate = async (
|
|||
language_code: "en",
|
||||
slug: body.entry.slug,
|
||||
});
|
||||
filterHasAttributes(libraryItem.libraryItems?.data[0].attributes?.subitem_of?.data, [
|
||||
filterHasAttributes(libraryItem.libraryItems?.data[0]?.attributes?.subitem_of?.data, [
|
||||
"attributes.slug",
|
||||
] as const).forEach((parentItem) => paths.push(`/library/${parentItem.attributes.slug}`));
|
||||
}
|
||||
|
@ -202,12 +203,12 @@ const Revalidate = async (
|
|||
slug: body.entry.slug,
|
||||
});
|
||||
|
||||
const folderSlug = content.contents?.data[0].attributes?.folder?.data?.attributes?.slug;
|
||||
const folderSlug = content.contents?.data[0]?.attributes?.folder?.data?.attributes?.slug;
|
||||
if (folderSlug) {
|
||||
paths.push(`/contents/folder/${folderSlug}`);
|
||||
}
|
||||
|
||||
filterHasAttributes(content.contents?.data[0].attributes?.ranged_contents?.data, [
|
||||
filterHasAttributes(content.contents?.data[0]?.attributes?.ranged_contents?.data, [
|
||||
"attributes.library_item.data.attributes.slug",
|
||||
] as const).forEach((ranged_content) => {
|
||||
const parentSlug = ranged_content.attributes.library_item.data.attributes.slug;
|
||||
|
@ -260,16 +261,16 @@ const Revalidate = async (
|
|||
slug: body.entry.slug,
|
||||
});
|
||||
const parentSlug =
|
||||
folder.contentsFolders?.data[0].attributes?.parent_folder?.data?.attributes?.slug;
|
||||
folder.contentsFolders?.data[0]?.attributes?.parent_folder?.data?.attributes?.slug;
|
||||
if (parentSlug) {
|
||||
paths.push(`/contents/folder/${parentSlug}`);
|
||||
}
|
||||
filterHasAttributes(folder.contentsFolders?.data[0].attributes?.subfolders?.data, [
|
||||
filterHasAttributes(folder.contentsFolders?.data[0]?.attributes?.subfolders?.data, [
|
||||
"attributes.slug",
|
||||
] as const).forEach((subfolder) =>
|
||||
paths.push(`/contents/folder/${subfolder.attributes.slug}`)
|
||||
);
|
||||
filterHasAttributes(folder.contentsFolders?.data[0].attributes?.contents?.data, [
|
||||
filterHasAttributes(folder.contentsFolders?.data[0]?.attributes?.contents?.data, [
|
||||
"attributes.slug",
|
||||
] as const).forEach((content) => paths.push(`/contents/${content.attributes.slug}`));
|
||||
}
|
||||
|
@ -308,7 +309,7 @@ const Revalidate = async (
|
|||
paths.push(`/archives/videos`);
|
||||
paths.push(`/archives/videos/v/${body.entry.uid}`);
|
||||
const video = await sdk.getVideo({ uid: body.entry.uid });
|
||||
const channelUid = video.videos?.data[0].attributes?.channel?.data?.attributes?.uid;
|
||||
const channelUid = video.videos?.data[0]?.attributes?.channel?.data?.attributes?.uid;
|
||||
if (isDefined(channelUid)) {
|
||||
paths.push(`/archives/videos/c/${channelUid}`);
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ import { getVideoThumbnailURL } from "helpers/videos";
|
|||
import { Icon } from "components/Ico";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { compareDate } from "helpers/date";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
|
@ -136,7 +136,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
|||
const channel = await sdk.getVideoChannel({
|
||||
channel: context.params && isDefined(context.params.uid) ? context.params.uid.toString() : "",
|
||||
});
|
||||
if (!channel.videoChannels?.data[0].attributes) return { notFound: true };
|
||||
if (!channel.videoChannels?.data[0]?.attributes) return { notFound: true };
|
||||
|
||||
channel.videoChannels.data[0].attributes.videos?.data
|
||||
.sort((a, b) => compareDate(a.attributes?.published_date, b.attributes?.published_date))
|
||||
|
|
|
@ -14,7 +14,7 @@ import { SubPanel } from "components/Containers/SubPanel";
|
|||
import { PreviewCard } from "components/PreviewCard";
|
||||
import { GetVideosPreviewQuery } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { getVideoThumbnailURL } from "helpers/videos";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
|
|
|
@ -12,7 +12,7 @@ import { SubPanel } from "components/Containers/SubPanel";
|
|||
import { GetVideoQuery } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettyDate, prettyShortenNumber } from "helpers/formatters";
|
||||
import { filterHasAttributes, isDefined } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined } from "helpers/asserts";
|
||||
import { getVideoFile } from "helpers/videos";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { GetStaticProps, GetStaticPaths, GetStaticPathsResult } from "next";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useCallback } from "react";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { isDefined, filterHasAttributes } from "helpers/others";
|
||||
import { isDefined, filterHasAttributes } from "helpers/asserts";
|
||||
import { ChronicleWithTranslations } from "types/types";
|
||||
import { AppLayout, AppLayoutRequired } from "components/AppLayout";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
|
|
|
@ -5,7 +5,7 @@ import { SubPanel } from "components/Containers/SubPanel";
|
|||
import { Icon } from "components/Ico";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { GetChroniclesChaptersQuery } from "graphql/generated";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { TranslatedChroniclesList } from "components/Chronicles/ChroniclesList";
|
||||
|
|
|
@ -21,7 +21,8 @@ import {
|
|||
prettySlug,
|
||||
} from "helpers/formatters";
|
||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||
import { filterHasAttributes, getStatusDescription, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { filterHasAttributes, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { ContentWithTranslations } from "types/types";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
|
@ -466,7 +467,7 @@ type FolderContents = NonNullable<
|
|||
const getPreviousContent = (contents: FolderContents, currentSlug: string) => {
|
||||
for (let index = 0; index < contents.length; index++) {
|
||||
const content = contents[index];
|
||||
if (content.attributes?.slug === currentSlug && index > 0) {
|
||||
if (content?.attributes?.slug === currentSlug && index > 0) {
|
||||
return contents[index - 1];
|
||||
}
|
||||
}
|
||||
|
@ -478,7 +479,7 @@ const getPreviousContent = (contents: FolderContents, currentSlug: string) => {
|
|||
const getNextContent = (contents: FolderContents, currentSlug: string) => {
|
||||
for (let index = 0; index < contents.length; index++) {
|
||||
const content = contents[index];
|
||||
if (content.attributes?.slug === currentSlug && index < contents.length - 1) {
|
||||
if (content?.attributes?.slug === currentSlug && index < contents.length - 1) {
|
||||
return contents[index + 1];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,10 +15,14 @@ import { WithLabel } from "components/Inputs/WithLabel";
|
|||
import { Button } from "components/Inputs/Button";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { Icon } from "components/Ico";
|
||||
import { filterDefined, filterHasAttributes, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import {
|
||||
filterDefined,
|
||||
filterHasAttributes,
|
||||
isDefinedAndNotEmpty,
|
||||
SelectiveNonNullable,
|
||||
} from "helpers/asserts";
|
||||
import { GetContentsQuery } from "graphql/generated";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
|
|
|
@ -4,7 +4,7 @@ import { AppLayout, AppLayoutRequired } from "components/AppLayout";
|
|||
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes } from "helpers/others";
|
||||
import { filterHasAttributes } from "helpers/asserts";
|
||||
import { GetContentsFolderQuery } from "graphql/generated";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
|
|
|
@ -6,7 +6,7 @@ import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/Cont
|
|||
import { ToolTip } from "components/ToolTip";
|
||||
import { DevGetContentsQuery } from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterDefined, filterHasAttributes } from "helpers/others";
|
||||
import { filterDefined, filterHasAttributes } from "helpers/asserts";
|
||||
import { Report, Severity } from "types/Report";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
|
@ -61,7 +61,8 @@ const CheckupContents = ({ contents, ...otherProps }: Props): JSX.Element => {
|
|||
? "bg-[#fff344] !opacity-100"
|
||||
: ""
|
||||
}
|
||||
text={Severity[line.severity]}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
text={Severity[line.severity] ?? "Unknown"}
|
||||
/>
|
||||
<ToolTip content={line.recommandation} placement="left">
|
||||
<p>{line.description}</p>
|
||||
|
|
|
@ -47,7 +47,7 @@ const CheckupLibraryItems = ({ libraryItems, ...otherProps }: Props): JSX.Elemen
|
|||
<div
|
||||
key={index}
|
||||
className="mb-2 grid
|
||||
grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center justify-items-start gap-2">
|
||||
grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center justify-items-start gap-2">
|
||||
<Button href={line.frontendUrl} className="w-4 text-xs" text="F" alwaysNewTab />
|
||||
<Button href={line.backendUrl} className="w-4 text-xs" text="B" alwaysNewTab />
|
||||
<p>{line.subitems.join(" -> ")}</p>
|
||||
|
@ -63,7 +63,8 @@ const CheckupLibraryItems = ({ libraryItems, ...otherProps }: Props): JSX.Elemen
|
|||
? "bg-[#fff344] !opacity-100"
|
||||
: ""
|
||||
}
|
||||
text={Severity[line.severity]}
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
text={Severity[line.severity] ?? "Unknown"}
|
||||
/>
|
||||
<ToolTip content={line.recommandation} placement="left">
|
||||
<p>{line.description}</p>
|
||||
|
|
|
@ -431,12 +431,7 @@ const DesignSystem = (props: Props): JSX.Element => {
|
|||
value={sliderState}
|
||||
max={100}
|
||||
onChange={(event) => {
|
||||
let value = 0;
|
||||
if (Array.isArray(event)) {
|
||||
value = event[0];
|
||||
} else {
|
||||
value = event;
|
||||
}
|
||||
const value = (Array.isArray(event) ? event[0] : event) ?? 0;
|
||||
setSliderState(() => value);
|
||||
}}
|
||||
/>
|
||||
|
|
|
@ -32,7 +32,7 @@ const replaceSelection = (
|
|||
const swapChar = (char: string, swaps: string[]): string => {
|
||||
for (let index = 0; index < swaps.length; index++) {
|
||||
if (char === swaps[index]) {
|
||||
return swaps[(index + 1) % swaps.length];
|
||||
return swaps[(index + 1) % swaps.length] ?? char;
|
||||
}
|
||||
}
|
||||
return char;
|
||||
|
|
|
@ -31,13 +31,13 @@ import {
|
|||
} from "helpers/formatters";
|
||||
import { ImageQuality } from "helpers/img";
|
||||
import { convertMmToInch } from "helpers/numbers";
|
||||
import { sortRangedContent } from "helpers/others";
|
||||
import {
|
||||
filterDefined,
|
||||
filterHasAttributes,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
sortRangedContent,
|
||||
} from "helpers/others";
|
||||
} from "helpers/asserts";
|
||||
import { useScrollTopOnChange } from "hooks/useScrollTopOnChange";
|
||||
import { isUntangibleGroupItem } from "helpers/libraryItem";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
|
|
|
@ -11,13 +11,8 @@ import {
|
|||
UploadImageFragment,
|
||||
} from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
getStatusDescription,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
sortRangedContent,
|
||||
} from "helpers/others";
|
||||
import { getStatusDescription, sortRangedContent } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { getLangui } from "graphql/fetchLocalData";
|
||||
import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/ContentPanel";
|
||||
|
@ -305,12 +300,7 @@ const LibrarySlug = ({
|
|||
max={10}
|
||||
value={filterSettings.teint * 10}
|
||||
onChange={(event) => {
|
||||
let value = 0;
|
||||
if (Array.isArray(event)) {
|
||||
value = event[0];
|
||||
} else {
|
||||
value = event;
|
||||
}
|
||||
const value = (Array.isArray(event) ? event[0] : event) ?? 0;
|
||||
setTeint(value / 10);
|
||||
}}
|
||||
/>
|
||||
|
@ -390,7 +380,7 @@ const LibrarySlug = ({
|
|||
? CUSTOM_DARK_DROPSHADOW
|
||||
: CUSTOM_LIGHT_DROPSHADOW,
|
||||
}}>
|
||||
{effectiveDisplayMode === "single" ? (
|
||||
{effectiveDisplayMode === "single" && isDefined(firstPage) ? (
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid grid-flow-col",
|
||||
|
@ -412,70 +402,73 @@ const LibrarySlug = ({
|
|||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid grid-flow-col",
|
||||
cIf(currentZoom <= 1, "cursor-pointer", "cursor-move")
|
||||
)}
|
||||
onClick={() => currentZoom <= 1 && handlePageNavigation("left")}
|
||||
style={{
|
||||
clipPath: leftSideClipPath,
|
||||
}}>
|
||||
{isSidePagesEnabled && (
|
||||
<div
|
||||
style={{
|
||||
width: leftSidePagesWidth,
|
||||
backgroundImage: `url(/reader/sidepages-${bookType}.webp)`,
|
||||
backgroundSize: `${
|
||||
(SIDEPAGES_PAGE_COUNT_ON_TEXTURE / leftSidePagesCount) * 100
|
||||
}% 100%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Img
|
||||
style={{ maxHeight: pageHeight, width: "auto" }}
|
||||
src={pageOrder === PageOrder.LeftToRight ? firstPage : secondPage}
|
||||
quality={pageQuality}
|
||||
/>
|
||||
<PageFilters page="left" bookType={bookType} options={filterSettings} />
|
||||
</div>
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid grid-flow-col",
|
||||
cIf(currentZoom <= 1, "cursor-pointer", "cursor-move")
|
||||
)}
|
||||
onClick={() => currentZoom <= 1 && handlePageNavigation("right")}
|
||||
style={{
|
||||
clipPath: rightSideClipPath,
|
||||
}}>
|
||||
<Img
|
||||
style={{ maxHeight: pageHeight, width: "auto" }}
|
||||
className={cIf(
|
||||
is1ColumnLayout,
|
||||
`max-h-[calc(100vh-5rem)]`,
|
||||
"max-h-[calc(100vh-4rem)]"
|
||||
isDefined(firstPage) &&
|
||||
isDefined(secondPage) && (
|
||||
<>
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid grid-flow-col",
|
||||
cIf(currentZoom <= 1, "cursor-pointer", "cursor-move")
|
||||
)}
|
||||
onClick={() => currentZoom <= 1 && handlePageNavigation("left")}
|
||||
style={{
|
||||
clipPath: leftSideClipPath,
|
||||
}}>
|
||||
{isSidePagesEnabled && (
|
||||
<div
|
||||
style={{
|
||||
width: leftSidePagesWidth,
|
||||
backgroundImage: `url(/reader/sidepages-${bookType}.webp)`,
|
||||
backgroundSize: `${
|
||||
(SIDEPAGES_PAGE_COUNT_ON_TEXTURE / leftSidePagesCount) * 100
|
||||
}% 100%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
src={pageOrder === PageOrder.LeftToRight ? secondPage : firstPage}
|
||||
quality={pageQuality}
|
||||
/>
|
||||
{isSidePagesEnabled && (
|
||||
<div
|
||||
style={{
|
||||
width: rightSidePagesWidth,
|
||||
backgroundImage: `url(/reader/sidepages-${bookType}.webp)`,
|
||||
backgroundPositionX: "right",
|
||||
backgroundSize: `${
|
||||
(SIDEPAGES_PAGE_COUNT_ON_TEXTURE / rightSidePagesCount) * 100
|
||||
}% 100%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<PageFilters page="right" bookType={bookType} options={filterSettings} />
|
||||
</div>
|
||||
</>
|
||||
<Img
|
||||
style={{ maxHeight: pageHeight, width: "auto" }}
|
||||
src={pageOrder === PageOrder.LeftToRight ? firstPage : secondPage}
|
||||
quality={pageQuality}
|
||||
/>
|
||||
<PageFilters page="left" bookType={bookType} options={filterSettings} />
|
||||
</div>
|
||||
<div
|
||||
className={cJoin(
|
||||
"relative grid grid-flow-col",
|
||||
cIf(currentZoom <= 1, "cursor-pointer", "cursor-move")
|
||||
)}
|
||||
onClick={() => currentZoom <= 1 && handlePageNavigation("right")}
|
||||
style={{
|
||||
clipPath: rightSideClipPath,
|
||||
}}>
|
||||
<Img
|
||||
style={{ maxHeight: pageHeight, width: "auto" }}
|
||||
className={cIf(
|
||||
is1ColumnLayout,
|
||||
`max-h-[calc(100vh-5rem)]`,
|
||||
"max-h-[calc(100vh-4rem)]"
|
||||
)}
|
||||
src={pageOrder === PageOrder.LeftToRight ? secondPage : firstPage}
|
||||
quality={pageQuality}
|
||||
/>
|
||||
{isSidePagesEnabled && (
|
||||
<div
|
||||
style={{
|
||||
width: rightSidePagesWidth,
|
||||
backgroundImage: `url(/reader/sidepages-${bookType}.webp)`,
|
||||
backgroundPositionX: "right",
|
||||
backgroundSize: `${
|
||||
(SIDEPAGES_PAGE_COUNT_ON_TEXTURE / rightSidePagesCount) * 100
|
||||
}% 100%`,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
<PageFilters page="right" bookType={bookType} options={filterSettings} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
)}
|
||||
</TransformComponent>
|
||||
</TransformWrapper>
|
||||
|
@ -498,12 +491,7 @@ const LibrarySlug = ({
|
|||
max={pages.length - 1}
|
||||
value={currentPageIndex - 1}
|
||||
onChange={(event) => {
|
||||
let value = 0;
|
||||
if (Array.isArray(event)) {
|
||||
value = event[0];
|
||||
} else {
|
||||
value = event;
|
||||
}
|
||||
const value = (Array.isArray(event) ? event[0] : event) ?? 0;
|
||||
changeCurrentPageIndex(() => value);
|
||||
}}
|
||||
/>
|
||||
|
@ -657,7 +645,7 @@ export const getStaticProps: GetStaticProps = async (context) => {
|
|||
bookType,
|
||||
pageWidth,
|
||||
itemSlug: item.libraryItems.data[0].attributes.slug,
|
||||
pageRatio: `${pages[0].width ?? 21} / ${pages[0].height ?? 29.7}`,
|
||||
pageRatio: `${pages[0]?.width ?? 21} / ${pages[0]?.height ?? 29.7}`,
|
||||
openGraph: getOpenGraph(
|
||||
langui,
|
||||
item.libraryItems.data[0].attributes.title,
|
||||
|
|
|
@ -21,10 +21,15 @@ import { isUntangibleGroupItem } from "helpers/libraryItem";
|
|||
import { PreviewCard } from "components/PreviewCard";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { ButtonGroup } from "components/Inputs/ButtonGroup";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty, isUndefined } from "helpers/others";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
isUndefined,
|
||||
SelectiveNonNullable,
|
||||
} from "helpers/asserts";
|
||||
import { convertPrice } from "helpers/numbers";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { compareDate } from "helpers/date";
|
||||
import { HorizontalLine } from "components/HorizontalLine";
|
||||
|
@ -142,11 +147,14 @@ const Library = ({ items, ...otherProps }: Props): JSX.Element => {
|
|||
return naturalCompare(titleA, titleB);
|
||||
}
|
||||
case 1: {
|
||||
const commonCurrency = currencies[0];
|
||||
if (isUndefined(commonCurrency)) return 0;
|
||||
|
||||
const priceA = a.attributes.price
|
||||
? convertPrice(a.attributes.price, currencies[0])
|
||||
? convertPrice(a.attributes.price, commonCurrency)
|
||||
: Infinity;
|
||||
const priceB = b.attributes.price
|
||||
? convertPrice(b.attributes.price, currencies[0])
|
||||
? convertPrice(b.attributes.price, commonCurrency)
|
||||
: Infinity;
|
||||
return priceA - priceB;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import { NextRouter, useRouter } from "next/router";
|
|||
import { PostPage } from "components/PostPage";
|
||||
import { getPostStaticProps, PostStaticProps } from "graphql/getPostStaticProps";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { Terminal } from "components/Cli/Terminal";
|
||||
import { PostWithTranslations } from "types/types";
|
||||
import { getDefaultPreferredLanguages, staticSmartLanguage } from "helpers/locales";
|
||||
|
|
|
@ -14,7 +14,7 @@ import { WithLabel } from "components/Inputs/WithLabel";
|
|||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { Button } from "components/Inputs/Button";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { filterHasAttributes, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { filterHasAttributes, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { compareDate } from "helpers/date";
|
||||
|
|
|
@ -10,7 +10,7 @@ import { ContentPanel, ContentPanelWidthSizes } from "components/Containers/Cont
|
|||
import { SubPanel } from "components/Containers/SubPanel";
|
||||
import DefinitionCard from "components/Wiki/DefinitionCard";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { WikiPageWithTranslations } from "types/types";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { prettySlug, sJoin } from "helpers/formatters";
|
||||
|
|
|
@ -13,12 +13,8 @@ import {
|
|||
} from "graphql/generated";
|
||||
import { getReadySdk } from "graphql/sdk";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import {
|
||||
filterHasAttributes,
|
||||
getStatusDescription,
|
||||
isDefined,
|
||||
isDefinedAndNotEmpty,
|
||||
} from "helpers/others";
|
||||
import { getStatusDescription } from "helpers/others";
|
||||
import { filterHasAttributes, isDefined, isDefinedAndNotEmpty } from "helpers/asserts";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { useSmartLanguage } from "hooks/useSmartLanguage";
|
||||
import { ToolTip } from "components/ToolTip";
|
||||
|
@ -156,7 +152,7 @@ const ChronologyEra = ({ id, title, description, chronologyItems }: ChronologyEr
|
|||
let currentYear = -Infinity;
|
||||
filterHasAttributes(chronologyItems, ["attributes"] as const).forEach((item) => {
|
||||
if (currentYear === item.attributes.year) {
|
||||
memo[memo.length - 1].push(item);
|
||||
memo[memo.length - 1]?.push(item);
|
||||
} else {
|
||||
currentYear = item.attributes.year;
|
||||
memo.push([item]);
|
||||
|
@ -214,7 +210,7 @@ interface ChronologyYearProps {
|
|||
const ChronologyYear = ({ items }: ChronologyYearProps) => (
|
||||
<div
|
||||
className="rounded-2xl target:my-4 target:bg-mid target:py-4"
|
||||
id={generateAnchor(items[0].attributes?.year)}>
|
||||
id={generateAnchor(items[0]?.attributes?.year)}>
|
||||
{filterHasAttributes(items, ["attributes.events"] as const).map((item, index) => (
|
||||
<ChronologyDate
|
||||
key={index}
|
||||
|
|
|
@ -15,10 +15,14 @@ import { Switch } from "components/Inputs/Switch";
|
|||
import { TextInput } from "components/Inputs/TextInput";
|
||||
import { WithLabel } from "components/Inputs/WithLabel";
|
||||
import { useDeviceSupportsHover } from "hooks/useMediaQuery";
|
||||
import { filterDefined, filterHasAttributes, isDefinedAndNotEmpty } from "helpers/others";
|
||||
import {
|
||||
filterDefined,
|
||||
filterHasAttributes,
|
||||
isDefinedAndNotEmpty,
|
||||
SelectiveNonNullable,
|
||||
} from "helpers/asserts";
|
||||
import { SmartList } from "components/SmartList";
|
||||
import { Select } from "components/Inputs/Select";
|
||||
import { SelectiveNonNullable } from "types/SelectiveNonNullable";
|
||||
import { prettySlug } from "helpers/formatters";
|
||||
import { getOpenGraph } from "helpers/openGraph";
|
||||
import { TranslatedPreviewCard } from "components/PreviewCard";
|
||||
|
|
|
@ -1,39 +0,0 @@
|
|||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
|
||||
type JoinDot<K extends string, P extends string> = `${K}${"" extends K ? "" : "."}${P}`;
|
||||
|
||||
export type PathDot<T, Acc extends string = ""> = T extends object
|
||||
? {
|
||||
[K in keyof T]: K extends string ? JoinDot<Acc, K> | PathDot<T[K], JoinDot<Acc, K>> : never;
|
||||
}[keyof T]
|
||||
: Acc;
|
||||
|
||||
type PathHead<T extends unknown[]> = T extends [infer head]
|
||||
? head
|
||||
: T extends [infer head, ...infer rest]
|
||||
? head
|
||||
: "";
|
||||
|
||||
type PathRest<T extends unknown[]> = T extends [infer head, ...infer rest]
|
||||
? rest extends []
|
||||
? never
|
||||
: rest
|
||||
: never;
|
||||
|
||||
type PathLast<T extends unknown[]> = T["length"] extends 1 ? true : false;
|
||||
|
||||
type Recursive<T, Path extends unknown[]> = PathHead<Path> extends keyof T
|
||||
? Omit<T, PathHead<Path>> & {
|
||||
[P in PathHead<Path>]-?: PathLast<Path> extends true
|
||||
? NonNullable<T[P]>
|
||||
: Recursive<NonNullable<T[P]>, PathRest<Path>>;
|
||||
}
|
||||
: T;
|
||||
|
||||
type Split<Str, Acc extends string[] = []> = Str extends `${infer Head}.${infer Rest}`
|
||||
? Split<Rest, [...Acc, Head]>
|
||||
: Str extends `${infer Last}`
|
||||
? [...Acc, Last]
|
||||
: never;
|
||||
|
||||
export type SelectiveNonNullable<T, P extends PathDot<T>> = Recursive<NonNullable<T>, Split<P>>;
|
|
@ -1,11 +1,15 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Type Checking
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedIndexedAccess": true,
|
||||
"strict": true,
|
||||
|
||||
"target": "ES6",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"importHelpers": true,
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
|
@ -16,7 +20,6 @@
|
|||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"baseUrl": "src"
|
||||
// "noUncheckedIndexedAccess": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
|
|
Loading…
Reference in New Issue