, Split>;
+
+export const isDefined = (t: T): t is NonNullable => t !== null && t !== undefined;
+
+export const isUndefined = (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[] | null | undefined): NonNullable[] =>
+ isUndefined(t) ? [] : (t.filter((item) => isDefined(item)) as NonNullable[]);
+
+export const filterHasAttributes = >(
+ t: T[] | null | undefined,
+ paths: readonly P[]
+): SelectiveNonNullable[] =>
+ isDefined(t)
+ ? (t.filter((item) => hasAttributes(item, paths)) as unknown as SelectiveNonNullable<
+ T,
+ typeof paths[number]
+ >[])
+ : [];
+
+const hasAttributes = (item: T, paths: readonly PathDot[]): boolean =>
+ isDefined(item) && paths.every((path) => hasAttribute(item, path));
+
+const hasAttribute = (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;
+};
diff --git a/src/helpers/component.tsx b/src/helpers/component.tsx
index c887de1..d96aa40 100644
--- a/src/helpers/component.tsx
+++ b/src/helpers/component.tsx
@@ -1,4 +1,4 @@
-import { isDefined } from "./others";
+import { isDefined } from "./asserts";
export interface Wrapper {
children: React.ReactNode;
diff --git a/src/helpers/date.ts b/src/helpers/date.ts
index 06b0e05..08f1396 100644
--- a/src/helpers/date.ts
+++ b/src/helpers/date.ts
@@ -1,4 +1,4 @@
-import { isUndefined } from "./others";
+import { isUndefined } from "./asserts";
import { DatePickerFragment } from "graphql/generated";
export const compareDate = (
diff --git a/src/helpers/description.ts b/src/helpers/description.ts
index 299fa3b..cd3347b 100644
--- a/src/helpers/description.ts
+++ b/src/helpers/description.ts
@@ -1,4 +1,4 @@
-import { filterDefined, isDefined, isDefinedAndNotEmpty } from "./others";
+import { filterDefined, isDefined, isDefinedAndNotEmpty } from "./asserts";
export const getDescription = (
description: string | null | undefined,
diff --git a/src/helpers/formatters.ts b/src/helpers/formatters.ts
index 3997f96..82cf34b 100644
--- a/src/helpers/formatters.ts
+++ b/src/helpers/formatters.ts
@@ -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": {
diff --git a/src/helpers/img.ts b/src/helpers/img.ts
index ae027d4..87dd917 100644
--- a/src/helpers/img.ts
+++ b/src/helpers/img.ts
@@ -27,13 +27,11 @@ const imageQualityProperties: Record = {
};
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 => {
diff --git a/src/helpers/libraryItem.ts b/src/helpers/libraryItem.ts
index cd74944..fb7e11b 100644
--- a/src/helpers/libraryItem.ts
+++ b/src/helpers/libraryItem.ts
@@ -1,4 +1,4 @@
-import { isDefined } from "./others";
+import { isDefined } from "./asserts";
export const isUntangibleGroupItem = (
metadata:
diff --git a/src/helpers/locales.ts b/src/helpers/locales.ts
index 3e63270..4d5a59c 100644
--- a/src/helpers/locales.ts
+++ b/src/helpers/locales.ts
@@ -1,4 +1,4 @@
-import { isDefined } from "./others";
+import { isDefined } from "./asserts";
export const getDefaultPreferredLanguages = (routerLocal: string, locales: string[]): string[] => {
let defaultPreferredLanguages: string[] = [];
diff --git a/src/helpers/openGraph.ts b/src/helpers/openGraph.ts
index b4063f7..c50197b 100644
--- a/src/helpers/openGraph.ts
+++ b/src/helpers/openGraph.ts
@@ -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";
diff --git a/src/helpers/others.ts b/src/helpers/others.ts
index e45f4c7..0d3c4a8 100644
--- a/src/helpers/others.ts
+++ b/src/helpers/others.ts
@@ -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 is NonNullable => t !== null && t !== undefined;
-
-export const isUndefined = (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[] | null | undefined): NonNullable[] =>
- isUndefined(t) ? [] : (t.filter((item) => isDefined(item)) as NonNullable[]);
-
-export const filterHasAttributes = >(
- t: T[] | null | undefined,
- paths: readonly P[]
-): SelectiveNonNullable[] =>
- isDefined(t)
- ? (t.filter((item) => hasAttributes(item, paths)) as unknown as SelectiveNonNullable<
- T,
- typeof paths[number]
- >[])
- : [];
-
-const hasAttributes = (item: T, paths: readonly PathDot[]): boolean =>
- isDefined(item) && paths.every((path) => hasAttribute(item, path));
-
-const hasAttribute = (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 = (
map: Map,
callbackfn: (key: K, value: V, index: number) => U,
@@ -99,7 +58,10 @@ export const iterateMap = (
};
export const arrayMove = (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;
};
diff --git a/src/helpers/terminal.ts b/src/helpers/terminal.ts
index 04937c7..05e327f 100644
--- a/src/helpers/terminal.ts
+++ b/src/helpers/terminal.ts
@@ -1,4 +1,4 @@
-import { isDefinedAndNotEmpty } from "./others";
+import { isDefinedAndNotEmpty } from "./asserts";
export const prettyTerminalUnderlinedTitle = (string: string | null | undefined): string =>
isDefinedAndNotEmpty(string)
diff --git a/src/hooks/useFullscreen.ts b/src/hooks/useFullscreen.ts
index a9fd4a0..72543ee 100644
--- a/src/hooks/useFullscreen.ts
+++ b/src/hooks/useFullscreen.ts
@@ -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
diff --git a/src/hooks/useIntersectionList.ts b/src/hooks/useIntersectionList.ts
index 04e44a4..4a02425 100644
--- a/src/hooks/useIntersectionList.ts
+++ b/src/hooks/useIntersectionList.ts
@@ -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]
);
diff --git a/src/hooks/useOnResize.ts b/src/hooks/useOnResize.ts
index ccfae39..678737d 100644
--- a/src/hooks/useOnResize.ts
+++ b/src/hooks/useOnResize.ts
@@ -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);
}
diff --git a/src/hooks/useScrollIntoView.ts b/src/hooks/useScrollIntoView.ts
index 5b44dc3..cc7631f 100644
--- a/src/hooks/useScrollIntoView.ts
+++ b/src/hooks/useScrollIntoView.ts
@@ -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;
diff --git a/src/hooks/useSmartLanguage.ts b/src/hooks/useSmartLanguage.ts
index 2e65aa5..1fd1106 100644
--- a/src/hooks/useSmartLanguage.ts
+++ b/src/hooks/useSmartLanguage.ts
@@ -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";
diff --git a/src/pages/api/revalidate.ts b/src/pages/api/revalidate.ts
index dd49406..2d2a502 100644
--- a/src/pages/api/revalidate.ts
+++ b/src/pages/api/revalidate.ts
@@ -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}`);
}
diff --git a/src/pages/archives/videos/c/[uid].tsx b/src/pages/archives/videos/c/[uid].tsx
index 4fe2507..75b7798 100644
--- a/src/pages/archives/videos/c/[uid].tsx
+++ b/src/pages/archives/videos/c/[uid].tsx
@@ -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))
diff --git a/src/pages/archives/videos/index.tsx b/src/pages/archives/videos/index.tsx
index 1f5fee0..51d9d2e 100644
--- a/src/pages/archives/videos/index.tsx
+++ b/src/pages/archives/videos/index.tsx
@@ -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";
diff --git a/src/pages/archives/videos/v/[uid].tsx b/src/pages/archives/videos/v/[uid].tsx
index 3537ebc..6e448a0 100644
--- a/src/pages/archives/videos/v/[uid].tsx
+++ b/src/pages/archives/videos/v/[uid].tsx
@@ -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";
diff --git a/src/pages/chronicles/[slug]/index.tsx b/src/pages/chronicles/[slug]/index.tsx
index f57cd89..bc8c842 100644
--- a/src/pages/chronicles/[slug]/index.tsx
+++ b/src/pages/chronicles/[slug]/index.tsx
@@ -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";
diff --git a/src/pages/chronicles/index.tsx b/src/pages/chronicles/index.tsx
index 52ff422..5a437fe 100644
--- a/src/pages/chronicles/index.tsx
+++ b/src/pages/chronicles/index.tsx
@@ -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";
diff --git a/src/pages/contents/[slug].tsx b/src/pages/contents/[slug].tsx
index b79bdab..317cc07 100644
--- a/src/pages/contents/[slug].tsx
+++ b/src/pages/contents/[slug].tsx
@@ -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];
}
}
diff --git a/src/pages/contents/all.tsx b/src/pages/contents/all.tsx
index 057cebd..78bcfe0 100644
--- a/src/pages/contents/all.tsx
+++ b/src/pages/contents/all.tsx
@@ -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";
diff --git a/src/pages/contents/folder/[slug].tsx b/src/pages/contents/folder/[slug].tsx
index 5974f34..c15459f 100644
--- a/src/pages/contents/folder/[slug].tsx
+++ b/src/pages/contents/folder/[slug].tsx
@@ -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";
diff --git a/src/pages/dev/checkup/contents.tsx b/src/pages/dev/checkup/contents.tsx
index 30df295..b8deb85 100644
--- a/src/pages/dev/checkup/contents.tsx
+++ b/src/pages/dev/checkup/contents.tsx
@@ -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"}
/>
{line.description}
diff --git a/src/pages/dev/checkup/libraryitems.tsx b/src/pages/dev/checkup/libraryitems.tsx
index d43315f..0efd694 100644
--- a/src/pages/dev/checkup/libraryitems.tsx
+++ b/src/pages/dev/checkup/libraryitems.tsx
@@ -47,7 +47,7 @@ const CheckupLibraryItems = ({ libraryItems, ...otherProps }: Props): JSX.Elemen
+ grid-cols-[2em,3em,2fr,1fr,0.5fr,0.5fr,2fr] items-center justify-items-start gap-2">
{line.subitems.join(" -> ")}
@@ -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"}
/>
{line.description}
diff --git a/src/pages/dev/showcase/design-system.tsx b/src/pages/dev/showcase/design-system.tsx
index 54e2671..abf76dc 100644
--- a/src/pages/dev/showcase/design-system.tsx
+++ b/src/pages/dev/showcase/design-system.tsx
@@ -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);
}}
/>
diff --git a/src/pages/dev/transcript.tsx b/src/pages/dev/transcript.tsx
index 4c76a7b..9401a65 100644
--- a/src/pages/dev/transcript.tsx
+++ b/src/pages/dev/transcript.tsx
@@ -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;
diff --git a/src/pages/library/[slug]/index.tsx b/src/pages/library/[slug]/index.tsx
index 903feeb..a1400e1 100644
--- a/src/pages/library/[slug]/index.tsx
+++ b/src/pages/library/[slug]/index.tsx
@@ -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";
diff --git a/src/pages/library/[slug]/reader.tsx b/src/pages/library/[slug]/reader.tsx
index 68f99a7..9d405d5 100644
--- a/src/pages/library/[slug]/reader.tsx
+++ b/src/pages/library/[slug]/reader.tsx
@@ -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) ? (
) : (
- <>
- currentZoom <= 1 && handlePageNavigation("left")}
- style={{
- clipPath: leftSideClipPath,
- }}>
- {isSidePagesEnabled && (
-
- )}
-
-
-
-
- currentZoom <= 1 && handlePageNavigation("right")}
- style={{
- clipPath: rightSideClipPath,
- }}>
-
+
currentZoom <= 1 && handlePageNavigation("left")}
+ style={{
+ clipPath: leftSideClipPath,
+ }}>
+ {isSidePagesEnabled && (
+
)}
- src={pageOrder === PageOrder.LeftToRight ? secondPage : firstPage}
- quality={pageQuality}
- />
- {isSidePagesEnabled && (
-
- )}
-
-
- >
+
+
+
+ currentZoom <= 1 && handlePageNavigation("right")}
+ style={{
+ clipPath: rightSideClipPath,
+ }}>
+
+ {isSidePagesEnabled && (
+
+ )}
+
+
+
+ >
+ )
)}
@@ -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,
diff --git a/src/pages/library/index.tsx b/src/pages/library/index.tsx
index 45b8a0d..328b756 100644
--- a/src/pages/library/index.tsx
+++ b/src/pages/library/index.tsx
@@ -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;
}
diff --git a/src/pages/news/[slug].tsx b/src/pages/news/[slug].tsx
index 67bd2fa..e96945b 100644
--- a/src/pages/news/[slug].tsx
+++ b/src/pages/news/[slug].tsx
@@ -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";
diff --git a/src/pages/news/index.tsx b/src/pages/news/index.tsx
index 344df0f..e919324 100644
--- a/src/pages/news/index.tsx
+++ b/src/pages/news/index.tsx
@@ -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";
diff --git a/src/pages/wiki/[slug]/index.tsx b/src/pages/wiki/[slug]/index.tsx
index 3c7508e..88b130c 100644
--- a/src/pages/wiki/[slug]/index.tsx
+++ b/src/pages/wiki/[slug]/index.tsx
@@ -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";
diff --git a/src/pages/wiki/chronology.tsx b/src/pages/wiki/chronology.tsx
index 95d2f8e..097441a 100644
--- a/src/pages/wiki/chronology.tsx
+++ b/src/pages/wiki/chronology.tsx
@@ -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) => (
+ id={generateAnchor(items[0]?.attributes?.year)}>
{filterHasAttributes(items, ["attributes.events"] as const).map((item, index) => (
= `${K}${"" extends K ? "" : "."}${P}`;
-
-export type PathDot = T extends object
- ? {
- [K in keyof T]: K extends string ? JoinDot | PathDot> : never;
- }[keyof T]
- : Acc;
-
-type PathHead = T extends [infer head]
- ? head
- : T extends [infer head, ...infer rest]
- ? head
- : "";
-
-type PathRest = T extends [infer head, ...infer rest]
- ? rest extends []
- ? never
- : rest
- : never;
-
-type PathLast = T["length"] extends 1 ? true : false;
-
-type Recursive = PathHead extends keyof T
- ? Omit> & {
- [P in PathHead]-?: PathLast extends true
- ? NonNullable
- : Recursive, PathRest>;
- }
- : T;
-
-type Split = Str extends `${infer Head}.${infer Rest}`
- ? Split
- : Str extends `${infer Last}`
- ? [...Acc, Last]
- : never;
-
-export type SelectiveNonNullable> = Recursive, Split>;
diff --git a/tsconfig.json b/tsconfig.json
index 5fb3c5d..b42ab78 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -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"]