Improved the wiki section

This commit is contained in:
DrMint 2022-07-11 02:35:09 +02:00
parent bb42e2a56f
commit d1d055de29
7 changed files with 278 additions and 91 deletions

View File

@ -1,4 +1,10 @@
import { Dispatch, Fragment, SetStateAction, useState } from "react";
import {
Dispatch,
Fragment,
SetStateAction,
useCallback,
useState,
} from "react";
import { Ico, Icon } from "components/Ico";
import { cIf, cJoin } from "helpers/className";
import { useToggle } from "hooks/useToggle";
@ -29,6 +35,11 @@ export const Select = ({
const [opened, setOpened] = useState(false);
const toggleOpened = useToggle(setOpened);
const tryToggling = useCallback(() => {
const optionCount = options.length + (state === -1 ? 1 : 0);
if (optionCount > 1) toggleOpened();
}, [options.length, state, toggleOpened]);
return (
<div
className={cJoin(
@ -45,7 +56,7 @@ export const Select = ({
cIf(opened, "rounded-b-none bg-highlight outline-[transparent]")
)}
>
<p onClick={toggleOpened} className="w-full">
<p onClick={tryToggling} className="w-full">
{state === -1 ? "—" : options[state]}
</p>
{state >= 0 && allowEmpty && (
@ -59,7 +70,7 @@ export const Select = ({
/>
)}
<Ico
onClick={toggleOpened}
onClick={tryToggling}
icon={opened ? Icon.ArrowDropUp : Icon.ArrowDropDown}
/>
</div>

View File

@ -4,6 +4,8 @@ import { ToolTip } from "components/ToolTip";
import { AppStaticProps } from "graphql/getAppStaticProps";
import { getStatusDescription } from "helpers/others";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import Link from "next/link";
import { Button } from "components/Inputs/Button";
/*
*
@ -11,7 +13,10 @@ import { useSmartLanguage } from "hooks/useSmartLanguage";
*/
interface Props {
source?: string;
source?: {
name?: string;
url?: string;
};
translations: {
language: string | undefined;
definition: string | null | undefined;
@ -79,9 +84,16 @@ const DefinitionCard = ({
)}
</div>
<p className="italic">{`${langui.source}: ${source}`}</p>
<p>{selectedTranslation?.definition}</p>
{source?.url && source.name && (
<Link href={source.url}>
<div className="flex place-items-center gap-2 mt-3">
<p>{langui.source}: </p>
<Button size="small" text={source.name} />
</div>
</Link>
)}
</>
);
};

View File

@ -0,0 +1,24 @@
fragment source on Source {
name
content {
data {
attributes {
slug
}
}
}
ranged_content {
data {
attributes {
slug
library_item {
data {
attributes {
slug
}
}
}
}
}
}
}

View File

@ -20,6 +20,24 @@ query getWikiPage($slug: String, $language_code: String) {
}
}
}
tags {
data {
id
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
translations {
title
aliases {
@ -73,7 +91,7 @@ query getWikiPage($slug: String, $language_code: String) {
source {
data {
attributes {
name
...source
}
}
}

View File

@ -1,4 +1,4 @@
query getWikiPagesPreviews {
query getWikiPagesPreviews($language_code: String) {
wikiPages(pagination: { limit: -1 }) {
data {
id
@ -20,6 +20,24 @@ query getWikiPagesPreviews {
}
}
}
tags {
data {
id
attributes {
slug
titles(filters: { language: { code: { eq: $language_code } } }) {
language {
data {
attributes {
code
}
}
}
title
}
}
}
}
translations {
title
aliases {

View File

@ -23,6 +23,9 @@ import {
} from "helpers/others";
import { WikiPageWithTranslations } from "helpers/types";
import { useSmartLanguage } from "hooks/useSmartLanguage";
import { prettySlug } from "helpers/formatters";
import { useLightBox } from "hooks/useLightBox";
import { getAssetURL, ImageQuality } from "helpers/img";
interface Props extends AppStaticProps {
page: WikiPageWithTranslations;
@ -45,6 +48,8 @@ const WikiPage = ({
),
});
const [openLightBox, LightBox] = useLightBox();
const subPanel = useMemo(
() => (
<SubPanel>
@ -63,6 +68,8 @@ const WikiPage = ({
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Large}>
<LightBox />
<ReturnButton
href={`/wiki`}
title={langui.wiki}
@ -78,7 +85,7 @@ const WikiPage = ({
<p className="mr-3 text-center text-2xl">
{`(${selectedTranslation.aliases
.map((alias) => alias?.alias)
.join(", ")})`}
.join("")})`}
</p>
)}
<LanguageSwitcher {...languageSwitcherProps} />
@ -93,19 +100,63 @@ const WikiPage = ({
text-center"
>
{page.thumbnail?.data?.attributes && (
<Img image={page.thumbnail.data.attributes} />
<Img
image={page.thumbnail.data.attributes}
quality={ImageQuality.Medium}
className="w-full cursor-pointer"
onClick={() => {
if (page.thumbnail?.data?.attributes?.url) {
openLightBox([
getAssetURL(
page.thumbnail.data.attributes.url,
ImageQuality.Large
),
]);
}
}}
/>
)}
<div className="my-4 grid gap-4 p-4">
<p className="font-headers text-xl font-bold">
{langui.categories}
</p>
<div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(page.categories?.data, [
"attributes",
] as const).map((category) => (
<Chip key={category.id} text={category.attributes.name} />
))}
</div>
{page.categories?.data && page.categories.data.length > 0 && (
<>
<p className="font-headers text-xl font-bold">
{langui.categories}
</p>
<div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(page.categories.data, [
"attributes",
] as const).map((category) => (
<Chip
key={category.id}
text={category.attributes.name}
/>
))}
</div>
</>
)}
{page.tags?.data && page.tags.data.length > 0 && (
<>
<p className="font-headers text-xl font-bold">
{/* TODO: Add Tags to langui */}
{"Tags"}
</p>
<div className="flex flex-row flex-wrap place-content-center gap-2">
{filterHasAttributes(page.tags?.data, [
"attributes",
] as const).map((tag) => (
<Chip
key={tag.id}
text={
tag.attributes.titles?.[0]?.title ??
prettySlug(tag.attributes.slug)
}
/>
))}
</div>
</>
)}
</div>
</div>
@ -124,7 +175,13 @@ const WikiPage = ({
<>
<DefinitionCard
key={index}
source={definition.source?.data?.attributes?.name}
source={{
name: definition.source?.data?.attributes?.name,
url: definition.source?.data?.attributes?.content?.data
?.attributes?.slug
? `/contents/${definition.source.data.attributes.content.data.attributes.slug}`
: `/library/${definition.source?.data?.attributes?.ranged_content?.data?.attributes?.library_item?.data?.attributes?.slug}`,
}}
translations={definition.translations.map((translation) => ({
language: translation?.language?.data?.attributes?.code,
definition: translation?.definition,
@ -149,9 +206,7 @@ const WikiPage = ({
languageSwitcherProps,
languages,
langui,
page.categories?.data,
page.definitions,
page.thumbnail?.data?.attributes,
page,
selectedTranslation,
]
);

View File

@ -1,5 +1,5 @@
import { GetStaticProps } from "next";
import { Fragment, useMemo, useState } from "react";
import { useCallback, useMemo, useState } from "react";
import { AppLayout } from "components/AppLayout";
import { NavOption } from "components/PanelComponents/NavOption";
import { PanelHeader } from "components/PanelComponents/PanelHeader";
@ -7,7 +7,7 @@ import { SubPanel } from "components/Panels/SubPanel";
import { AppStaticProps, getAppStaticProps } from "graphql/getAppStaticProps";
import { Icon } from "components/Ico";
import { getReadySdk } from "graphql/sdk";
import { GetWikiPagesPreviewsQuery } from "graphql/generated";
import { GetWikiPageQuery, GetWikiPagesPreviewsQuery } from "graphql/generated";
import {
ContentPanel,
ContentPanelWidthSizes,
@ -19,8 +19,12 @@ import { Switch } from "components/Inputs/Switch";
import { TextInput } from "components/Inputs/TextInput";
import { WithLabel } from "components/Inputs/WithLabel";
import { useMediaHoverable } from "hooks/useMediaQuery";
import { filterHasAttributes } from "helpers/others";
import { filterDefined, filterHasAttributes, isDefined } from "helpers/others";
import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholder";
import { SmartList } from "components/SmartList";
import { Select } from "components/Inputs/Select";
import { SelectiveNonNullable } from "helpers/types/SelectiveNonNullable";
import { prettySlug } from "helpers/formatters";
/*
*
@ -30,6 +34,7 @@ import { ContentPlaceholder } from "components/PanelComponents/ContentPlaceholde
const DEFAULT_FILTERS_STATE = {
searchName: "",
keepInfoVisible: true,
groupingMethod: -1,
};
/*
@ -52,13 +57,13 @@ const Wiki = ({
const [searchName, setSearchName] = useState(
DEFAULT_FILTERS_STATE.searchName
);
const [keepInfoVisible, setKeepInfoVisible] = useState(
DEFAULT_FILTERS_STATE.keepInfoVisible
const [groupingMethod, setGroupingMethod] = useState<number>(
DEFAULT_FILTERS_STATE.groupingMethod
);
const filteredPages = useMemo(
() => filterPages(pages, searchName),
[pages, searchName]
const [keepInfoVisible, setKeepInfoVisible] = useState(
DEFAULT_FILTERS_STATE.keepInfoVisible
);
const subPanel = useMemo(
@ -77,6 +82,19 @@ const Wiki = ({
setState={setSearchName}
/>
<WithLabel
label={langui.group_by}
input={
<Select
className="w-full"
options={[langui.category ?? ""]}
state={groupingMethod}
setState={setGroupingMethod}
allowEmpty
/>
}
/>
{hoverable && (
<WithLabel
label={langui.always_show_info}
@ -104,59 +122,105 @@ const Wiki = ({
<NavOption title={langui.chronology} url="/wiki/chronology" border />
</SubPanel>
),
[hoverable, keepInfoVisible, langui, searchName]
[groupingMethod, hoverable, keepInfoVisible, langui, searchName]
);
const groupingFunction = useCallback(
(
item: SelectiveNonNullable<
NonNullable<GetWikiPageQuery["wikiPages"]>["data"][number],
"attributes" | "id"
>
): string[] => {
switch (groupingMethod) {
case 0: {
const categories = filterHasAttributes(
item.attributes.categories?.data,
["attributes"] as const
);
if (categories.length > 0) {
return categories.map((category) => category.attributes.name);
}
return [langui.no_category ?? "No category"];
}
default: {
return [""];
}
}
},
[groupingMethod, langui]
);
const contentPanel = useMemo(
() => (
<ContentPanel width={ContentPanelWidthSizes.Full}>
<div
className="grid grid-cols-2 items-end gap-8
desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))] mobile:gap-4"
>
{filteredPages.length === 0 && (
<SmartList
items={filterHasAttributes(pages, ["id", "attributes"] as const)}
getItemId={(item) => item.id}
renderItem={({ item }) => (
<>
{isDefined(item.attributes.translations) && (
<TranslatedPreviewCard
href={`/wiki/${item.attributes.slug}`}
translations={item.attributes.translations.map(
(translation) => ({
title: translation?.title,
subtitle:
translation?.aliases && translation.aliases.length > 0
? translation.aliases
.map((alias) => alias?.alias)
.join("・")
: undefined,
description: translation?.summary,
language: translation?.language?.data?.attributes?.code,
})
)}
thumbnail={item.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio={"4/3"}
thumbnailRounded
thumbnailForceAspectRatio
languages={languages}
slug={item.attributes.slug}
keepInfoVisible={keepInfoVisible}
topChips={filterHasAttributes(item.attributes.tags?.data, [
"attributes",
] as const).map(
(tag) =>
tag.attributes.titles?.[0]?.title ??
prettySlug(tag.attributes.slug)
)}
bottomChips={filterHasAttributes(
item.attributes.categories?.data,
["attributes"] as const
).map((category) => category.attributes.short)}
/>
)}
</>
)}
renderWhenEmpty={() => (
<ContentPlaceholder
message={langui.no_results_message ?? "No results"}
icon={Icon.ChevronLeft}
/>
)}
{filterHasAttributes(filteredPages, [
"id",
"attributes.translations",
] as const).map((page) => (
<Fragment key={page.id}>
<TranslatedPreviewCard
href={`/wiki/${page.attributes.slug}`}
translations={page.attributes.translations.map(
(translation) => ({
title: translation?.title,
subtitle:
translation?.aliases && translation.aliases.length > 0
? translation.aliases
.map((alias) => alias?.alias)
.join(" | ")
: undefined,
description: translation?.summary,
language: translation?.language?.data?.attributes?.code,
})
)}
thumbnail={page.attributes.thumbnail?.data?.attributes}
thumbnailAspectRatio={"4/3"}
thumbnailRounded
thumbnailForceAspectRatio
languages={languages}
slug={page.attributes.slug}
keepInfoVisible={keepInfoVisible}
bottomChips={page.attributes.categories?.data.map(
(category) => category.attributes?.short ?? ""
)}
/>
</Fragment>
))}
</div>
langui={langui}
className="grid-cols-2 desktop:grid-cols-[repeat(auto-fill,_minmax(20rem,1fr))]"
searchingTerm={searchName}
searchingBy={(item) =>
filterDefined(item.attributes.translations)
.map(
(translation) =>
`${translation.title} ${filterDefined(translation.aliases)
.map((alias) => alias.alias)
.join(" ")}`
)
.join(" ")
}
groupingFunction={groupingFunction}
/>
</ContentPanel>
),
[filteredPages, keepInfoVisible, languages, langui]
[groupingFunction, keepInfoVisible, languages, langui, pages, searchName]
);
return (
@ -180,7 +244,9 @@ export default Wiki;
export const getStaticProps: GetStaticProps = async (context) => {
const sdk = getReadySdk();
const pages = await sdk.getWikiPagesPreviews({});
const pages = await sdk.getWikiPagesPreviews({
language_code: context.locale ?? "en",
});
if (!pages.wikiPages?.data) return { notFound: true };
const props: Props = {
...(await getAppStaticProps(context)),
@ -202,20 +268,3 @@ const sortPages = (pages: Props["pages"]): Props["pages"] =>
const slugB = b.attributes?.slug ?? "";
return slugA.localeCompare(slugB);
});
// ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
const filterPages = (posts: Props["pages"], searchName: string) =>
posts.filter((post) => {
if (searchName.length > 1) {
if (
post.attributes?.translations?.[0]?.title
.toLowerCase()
.includes(searchName.toLowerCase()) === true
) {
return true;
}
return false;
}
return true;
});