Added timeline page
This commit is contained in:
parent
606c3cc53f
commit
0c4d5e4007
2
TODO.md
2
TODO.md
|
@ -12,7 +12,6 @@
|
||||||
- [Collectibles] Create page for scans
|
- [Collectibles] Create page for scans
|
||||||
- When the tags overflow, the tag group name should be align start (see http://localhost:12499/en/pages/magnitude-negative-chapter-1)
|
- When the tags overflow, the tag group name should be align start (see http://localhost:12499/en/pages/magnitude-negative-chapter-1)
|
||||||
- [SDK] create a initPayload() that return a payload sdk (and stop hard wirring to ENV or node-cache)
|
- [SDK] create a initPayload() that return a payload sdk (and stop hard wirring to ENV or node-cache)
|
||||||
- [Payload] Compare current package.json with fresh install of create-payload-app
|
|
||||||
|
|
||||||
## Long term
|
## Long term
|
||||||
|
|
||||||
|
@ -21,7 +20,6 @@
|
||||||
- Grid view (all files)
|
- Grid view (all files)
|
||||||
- Web archives
|
- Web archives
|
||||||
- Videos
|
- Videos
|
||||||
- Timeline page
|
|
||||||
- Contact page
|
- Contact page
|
||||||
- About us page
|
- About us page
|
||||||
- Global search function
|
- Global search function
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 455 KiB |
|
@ -48,7 +48,7 @@ const contactLabel = `${t("footer.socials.contact.title")} - ${t(
|
||||||
<Icon name="material-symbols:history" />
|
<Icon name="material-symbols:history" />
|
||||||
<p>{"Changelog"}</p>
|
<p>{"Changelog"}</p>
|
||||||
</a>
|
</a>
|
||||||
<a href="/timeline" class="DEV_TODO">
|
<a href="/timeline">
|
||||||
<Icon name="material-symbols:calendar-month" />
|
<Icon name="material-symbols:calendar-month" />
|
||||||
<p>{t("footer.links.timeline.title")}</p>
|
<p>{t("footer.links.timeline.title")}</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -499,6 +499,10 @@ const { currentTheme } = Astro.locals;
|
||||||
font-size: 24px;
|
font-size: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
> h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
> h2,
|
> h2,
|
||||||
> h3,
|
> h3,
|
||||||
> h4,
|
> h4,
|
||||||
|
@ -548,3 +552,17 @@ const { currentTheme } = Astro.locals;
|
||||||
Array.from(newDocument.querySelectorAll(".when-no-js")).forEach((node) => node.remove());
|
Array.from(newDocument.querySelectorAll(".when-no-js")).forEach((node) => node.remove());
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script is:inline data-astro-rerun>
|
||||||
|
document.querySelectorAll("a").forEach((element) => {
|
||||||
|
const href = element.getAttribute("href");
|
||||||
|
if (!href || !href.startsWith("#")) return;
|
||||||
|
const heading = document.getElementById(href.substring(1));
|
||||||
|
if (!heading) return;
|
||||||
|
|
||||||
|
element.addEventListener("click", (event) => {
|
||||||
|
heading.scrollIntoView({ behavior: "smooth" });
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
---
|
||||||
|
import type { EndpointRecorder } from "src/shared/payload/payload-sdk";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
translators?: EndpointRecorder[] | undefined;
|
||||||
|
transcribers?: EndpointRecorder[] | undefined;
|
||||||
|
proofreaders?: EndpointRecorder[] | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { translators = [], transcribers = [], proofreaders = [] } = Astro.props;
|
||||||
|
const { t } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
|
||||||
|
const tagGroups = [];
|
||||||
|
|
||||||
|
if (translators.length > 0) {
|
||||||
|
tagGroups.push({
|
||||||
|
name: t("global.credits.translators"),
|
||||||
|
values: translators,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (transcribers.length > 0) {
|
||||||
|
tagGroups.push({
|
||||||
|
name: t("global.credits.transcribers"),
|
||||||
|
values: transcribers,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proofreaders.length > 0) {
|
||||||
|
tagGroups.push({
|
||||||
|
name: t("global.credits.proofreaders"),
|
||||||
|
values: proofreaders,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
---
|
||||||
|
|
||||||
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
|
<div id="tags">
|
||||||
|
{
|
||||||
|
tagGroups.map(({ name, values }) => (
|
||||||
|
<div>
|
||||||
|
<p>{name}</p>
|
||||||
|
{values.map(({ username }) => username).join(", ")}
|
||||||
|
</div>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* ------------------------------------------- CSS -------------------------------------------- */}
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#tags {
|
||||||
|
margin-top: 0.5em;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em 1.5em;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 80%;
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
& > p {
|
||||||
|
color: var(--color-base-750);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -28,7 +28,7 @@ const title = (() => {
|
||||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<li data-prefix={entry.prefix}>
|
<li data-prefix={entry.prefix}>
|
||||||
<a href={`#${entry.prefix}`} class="pressable-link table-of-content-item">{title}</a>
|
<a href={`#${entry.prefix}`} class="pressable-link">{title}</a>
|
||||||
{
|
{
|
||||||
entry.children.length > 0 && (
|
entry.children.length > 0 && (
|
||||||
<ol>
|
<ol>
|
||||||
|
@ -61,19 +61,3 @@ const title = (() => {
|
||||||
line-height: 125%;
|
line-height: 125%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
{/* ------------------------------------------- JS --------------------------------------------- */}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.querySelectorAll(".table-of-content-item").forEach((element) => {
|
|
||||||
const href = element.getAttribute("href")?.substring(1);
|
|
||||||
if (!href) return;
|
|
||||||
const heading = document.getElementById(href);
|
|
||||||
if (!heading) return;
|
|
||||||
|
|
||||||
element.addEventListener("click", (event) => {
|
|
||||||
heading.scrollIntoView({ behavior: "smooth" });
|
|
||||||
event.preventDefault();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
import type { WordingKey } from "src/i18n/wordings-keys";
|
||||||
|
|
||||||
|
const timelineEras: { name: WordingKey; start: number; end: number }[] = [
|
||||||
|
{ name: "timeline.eras.cataclysm", start: 856, end: 856 },
|
||||||
|
{
|
||||||
|
name: "timeline.eras.drakengard3",
|
||||||
|
start: 997,
|
||||||
|
end: 1000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timeline.eras.drakengard",
|
||||||
|
start: 1001,
|
||||||
|
end: 1099,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timeline.eras.drakengard2",
|
||||||
|
start: 1100,
|
||||||
|
end: 1117,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timeline.eras.nier",
|
||||||
|
start: 2003,
|
||||||
|
end: 5012,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "timeline.eras.nierAutomata",
|
||||||
|
start: 5012,
|
||||||
|
end: 12543,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const dataConfig = {
|
||||||
|
timeline: {
|
||||||
|
yearsWithABreakBefore: [856, 997, 1001, 1118, 1957, 2003, 2049, 2050, 3361, 3463],
|
||||||
|
eras: timelineEras,
|
||||||
|
},
|
||||||
|
};
|
|
@ -1,5 +1,7 @@
|
||||||
import type { WordingKey } from "src/i18n/wordings-keys";
|
import type { WordingKey } from "src/i18n/wordings-keys";
|
||||||
|
import type { ChronologyEvent } from "src/shared/payload/payload-sdk";
|
||||||
import { cache } from "src/utils/cachedPayload";
|
import { cache } from "src/utils/cachedPayload";
|
||||||
|
import { capitalize } from "src/utils/format";
|
||||||
|
|
||||||
export const defaultLocale = "en";
|
export const defaultLocale = "en";
|
||||||
|
|
||||||
|
@ -105,9 +107,7 @@ export const getI18n = async (locale: string) => {
|
||||||
return template;
|
return template;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getLocalizedMatch = <T extends { language: string }>(
|
const getLocalizedMatch = <T extends { language: string }>(options: T[]): T =>
|
||||||
options: T[]
|
|
||||||
): Omit<T, "language"> & { language?: string } =>
|
|
||||||
options.find(({ language }) => language === locale) ??
|
options.find(({ language }) => language === locale) ??
|
||||||
options.find(({ language }) => language === defaultLocale) ??
|
options.find(({ language }) => language === defaultLocale) ??
|
||||||
options[0]!; // We will consider that there will always be at least one option.
|
options[0]!; // We will consider that there will always be at least one option.
|
||||||
|
@ -129,8 +129,10 @@ export const getI18n = async (locale: string) => {
|
||||||
const formatPrice = (price: { amount: number; currency: string }): string =>
|
const formatPrice = (price: { amount: number; currency: string }): string =>
|
||||||
price.amount.toLocaleString(locale, { style: "currency", currency: price.currency });
|
price.amount.toLocaleString(locale, { style: "currency", currency: price.currency });
|
||||||
|
|
||||||
const formatDate = (date: Date): string =>
|
const formatDate = (
|
||||||
date.toLocaleDateString(locale, { dateStyle: "medium" });
|
date: Date,
|
||||||
|
options: Intl.DateTimeFormatOptions | undefined = { dateStyle: "medium" }
|
||||||
|
): string => date.toLocaleDateString(locale, options);
|
||||||
|
|
||||||
const formatInches = (sizeInMm: number): string => {
|
const formatInches = (sizeInMm: number): string => {
|
||||||
return (
|
return (
|
||||||
|
@ -156,6 +158,21 @@ export const getI18n = async (locale: string) => {
|
||||||
return number.toLocaleString(locale, options);
|
return number.toLocaleString(locale, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const formatTimelineDate = ({ year, month, day }: ChronologyEvent["date"]): string => {
|
||||||
|
const date = new Date();
|
||||||
|
date.setFullYear(year);
|
||||||
|
if (month) date.setMonth(month - 1);
|
||||||
|
if (day) date.setDate(day);
|
||||||
|
|
||||||
|
return capitalize(
|
||||||
|
formatDate(date, {
|
||||||
|
year: "numeric",
|
||||||
|
month: month ? "long" : undefined,
|
||||||
|
day: day ? "numeric" : undefined,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
t,
|
t,
|
||||||
getLocalizedMatch,
|
getLocalizedMatch,
|
||||||
|
@ -167,5 +184,6 @@ export const getI18n = async (locale: string) => {
|
||||||
formatGrams,
|
formatGrams,
|
||||||
formatMillimeters,
|
formatMillimeters,
|
||||||
formatNumber,
|
formatNumber,
|
||||||
|
formatTimelineDate,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -87,4 +87,21 @@ export type WordingKey =
|
||||||
| "global.loading"
|
| "global.loading"
|
||||||
| "pages.tableOfContent.sceneBreak"
|
| "pages.tableOfContent.sceneBreak"
|
||||||
| "pages.tableOfContent.break"
|
| "pages.tableOfContent.break"
|
||||||
| "global.languageOverride.availableLanguages";
|
| "global.languageOverride.availableLanguages"
|
||||||
|
| "timeline.title"
|
||||||
|
| "timeline.eras.drakengard3"
|
||||||
|
| "timeline.eras.drakengard"
|
||||||
|
| "timeline.eras.drakengard2"
|
||||||
|
| "timeline.eras.nier"
|
||||||
|
| "timeline.eras.nierAutomata"
|
||||||
|
| "timeline.eras.cataclysm"
|
||||||
|
| "timeline.description"
|
||||||
|
| "timeline.notes.title"
|
||||||
|
| "timeline.notes.content"
|
||||||
|
| "timeline.priorCataclysmNote.title"
|
||||||
|
| "timeline.priorCataclysmNote.content"
|
||||||
|
| "timeline.jumpTo"
|
||||||
|
| "timeline.year.during"
|
||||||
|
| "timeline.eventFooter.languages"
|
||||||
|
| "timeline.eventFooter.sources"
|
||||||
|
| "timeline.eventFooter.note";
|
||||||
|
|
|
@ -0,0 +1,64 @@
|
||||||
|
---
|
||||||
|
import MasoTarget from "components/Maso/MasoTarget.astro";
|
||||||
|
import TimelineEventTranslation from "pages/[locale]/timeline/_components/TimelineEventTranslation.astro";
|
||||||
|
import TimelineLanguageOverride from "pages/[locale]/timeline/_components/TimelineLanguageOverride.astro";
|
||||||
|
import TimelineNote from "pages/[locale]/timeline/_components/TimelineNote.astro";
|
||||||
|
import TimelineSourcesButton from "pages/[locale]/timeline/_components/TimelineSourcesButton.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import { payload, type EndpointChronologyEvent } from "src/shared/payload/payload-sdk";
|
||||||
|
|
||||||
|
export const partial = true;
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
lang: string;
|
||||||
|
event: EndpointChronologyEvent;
|
||||||
|
id: string;
|
||||||
|
index: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const reqUrl = new URL(Astro.request.url);
|
||||||
|
const lang = Astro.props.lang ?? reqUrl.searchParams.get("lang")!;
|
||||||
|
const id = Astro.props.id ?? reqUrl.searchParams.get("id")!;
|
||||||
|
const index = Astro.props.index ?? parseInt(reqUrl.searchParams.get("index")!);
|
||||||
|
const event = Astro.props.event ?? (await payload.getChronologyEventByID(id));
|
||||||
|
const { sources, translations } = event.events[index]!;
|
||||||
|
|
||||||
|
const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
const { getLocalizedMatch } = await getI18n(lang);
|
||||||
|
const { title, description, notes, proofreaders, transcribers, translators } =
|
||||||
|
getLocalizedMatch(translations);
|
||||||
|
---
|
||||||
|
|
||||||
|
<MasoTarget>
|
||||||
|
<div class="event">
|
||||||
|
<TimelineEventTranslation title={title} description={description} />
|
||||||
|
<div id="bottom" class="when-js when-no-print">
|
||||||
|
{sources.length > 0 && <TimelineSourcesButton sources={sources} />}
|
||||||
|
{notes && <TimelineNote notes={notes} />}
|
||||||
|
<TimelineLanguageOverride
|
||||||
|
availableLanguages={translations.map(({ language }) => language)}
|
||||||
|
currentLang={lang}
|
||||||
|
proofreaders={proofreaders}
|
||||||
|
transcribers={transcribers}
|
||||||
|
translators={translators}
|
||||||
|
getPartialUrl={(locale) =>
|
||||||
|
getLocalizedUrl(`/api/timeline/partial?id=${id}&index=${index}&lang=${locale}`)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</MasoTarget>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.event {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1em;
|
||||||
|
|
||||||
|
& > #bottom {
|
||||||
|
display: flex;
|
||||||
|
place-items: start;
|
||||||
|
gap: 0.3em;
|
||||||
|
font-size: 85%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -6,6 +6,7 @@ import LibraryGrid from "./_components/LibraryGrid.astro";
|
||||||
import ChronicleCard from "./_components/ChronicleCard.astro";
|
import ChronicleCard from "./_components/ChronicleCard.astro";
|
||||||
import LinkCard from "./_components/LinkCard.astro";
|
import LinkCard from "./_components/LinkCard.astro";
|
||||||
import { getI18n } from "src/i18n/i18n";
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import { dataConfig } from "src/dataConfig";
|
||||||
|
|
||||||
const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||||
---
|
---
|
||||||
|
@ -113,15 +114,6 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||||
<p set:html={t("home.moreSection.description")} />
|
<p set:html={t("home.moreSection.description")} />
|
||||||
<div class="grid">
|
<div class="grid">
|
||||||
<div class="DEV_TODO">
|
<div class="DEV_TODO">
|
||||||
<LinkCard
|
|
||||||
icon="material-symbols:calendar-month-outline"
|
|
||||||
title={t("footer.links.timeline.title")}
|
|
||||||
subtitle={t("footer.links.timeline.subtitle", {
|
|
||||||
eraCount: 8,
|
|
||||||
eventCount: 358,
|
|
||||||
})}
|
|
||||||
href={getLocalizedUrl("/timeline")}
|
|
||||||
/>
|
|
||||||
<LinkCard
|
<LinkCard
|
||||||
icon="material-symbols:movie-outline"
|
icon="material-symbols:movie-outline"
|
||||||
title={t("footer.links.videos.title")}
|
title={t("footer.links.videos.title")}
|
||||||
|
@ -136,6 +128,16 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<LinkCard
|
||||||
|
icon="material-symbols:calendar-month-outline"
|
||||||
|
title={t("footer.links.timeline.title")}
|
||||||
|
subtitle={t("footer.links.timeline.subtitle", {
|
||||||
|
eraCount: dataConfig.timeline.eras.length,
|
||||||
|
eventCount: 358,
|
||||||
|
})}
|
||||||
|
href={getLocalizedUrl("/timeline")}
|
||||||
|
/>
|
||||||
|
|
||||||
<LinkCard
|
<LinkCard
|
||||||
icon="material-symbols:perm-media-outline"
|
icon="material-symbols:perm-media-outline"
|
||||||
title={t("footer.links.gallery.title")}
|
title={t("footer.links.gallery.title")}
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
---
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import type { EndpointChronologyEvent } from "src/shared/payload/payload-sdk";
|
||||||
|
import TimelineEventPartial from "../../api/timeline/partial.astro";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
event: EndpointChronologyEvent;
|
||||||
|
displayDate: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { event, displayDate } = Astro.props;
|
||||||
|
const { formatTimelineDate, t } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
|
||||||
|
const displayedDate =
|
||||||
|
!event.date.month && !event.date.day
|
||||||
|
? t("timeline.year.during", { year: formatTimelineDate(event.date) })
|
||||||
|
: formatTimelineDate(event.date);
|
||||||
|
---
|
||||||
|
|
||||||
|
<div class="event-container">
|
||||||
|
{displayDate && <h3>{displayedDate}</h3>}
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
event.events.map((_, index) => (
|
||||||
|
<TimelineEventPartial
|
||||||
|
event={event}
|
||||||
|
index={index}
|
||||||
|
id={event.id}
|
||||||
|
lang={Astro.locals.currentLocale}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.event-container {
|
||||||
|
&:has(h3) > div {
|
||||||
|
border-left: 1px solid var(--color-base-600);
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-block: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > h3 {
|
||||||
|
padding-bottom: 0.3em;
|
||||||
|
padding-inline: 0.2em;
|
||||||
|
color: var(--color-base-700);
|
||||||
|
border-bottom: 1px solid var(--color-base-600);
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,29 @@
|
||||||
|
---
|
||||||
|
import RichText from "components/RichText/RichText.astro";
|
||||||
|
import type { RichTextContent } from "src/shared/payload/payload-sdk";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
title?: string | undefined;
|
||||||
|
description?: RichTextContent | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { title, description } = Astro.props;
|
||||||
|
---
|
||||||
|
|
||||||
|
<div>
|
||||||
|
{title && <h4>{title}</h4>}
|
||||||
|
{description && <RichText content={description} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
div {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5em;
|
||||||
|
|
||||||
|
& > h4 {
|
||||||
|
font-size: 120%;
|
||||||
|
max-width: 35rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,66 @@
|
||||||
|
---
|
||||||
|
import { Icon } from "astro-icon/components";
|
||||||
|
import InlineCredits from "components/InlineCredits.astro";
|
||||||
|
import MasoActor from "components/Maso/MasoActor.astro";
|
||||||
|
import Tooltip from "components/Tooltip.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import type { EndpointRecorder } from "src/shared/payload/payload-sdk";
|
||||||
|
import { formatLocale } from "src/utils/format";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
currentLang: string;
|
||||||
|
availableLanguages: string[];
|
||||||
|
getPartialUrl: (locale: string) => string;
|
||||||
|
transcribers: EndpointRecorder[];
|
||||||
|
translators: EndpointRecorder[];
|
||||||
|
proofreaders: EndpointRecorder[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { availableLanguages, transcribers, proofreaders, translators, getPartialUrl, currentLang } =
|
||||||
|
Astro.props;
|
||||||
|
|
||||||
|
const { t } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
---
|
||||||
|
|
||||||
|
<Tooltip trigger="click">
|
||||||
|
<div id="tooltip-content" slot="tooltip-content">
|
||||||
|
{
|
||||||
|
availableLanguages.map((id) => (
|
||||||
|
<MasoActor href={getPartialUrl(id)}>
|
||||||
|
<p class:list={{ current: id === currentLang, "pressable-link": true }}>
|
||||||
|
{formatLocale(id)}
|
||||||
|
</p>
|
||||||
|
</MasoActor>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
<InlineCredits
|
||||||
|
translators={translators}
|
||||||
|
transcribers={transcribers}
|
||||||
|
proofreaders={proofreaders}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="pressable-label">
|
||||||
|
<Icon name="material-symbols:translate" />
|
||||||
|
<p>
|
||||||
|
{
|
||||||
|
t("timeline.eventFooter.languages", {
|
||||||
|
count: availableLanguages.length,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#tooltip-content {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
font-size: 1rem;
|
||||||
|
|
||||||
|
& .current {
|
||||||
|
color: var(--color-base-750);
|
||||||
|
text-decoration: underline 0.08em var(--color-base-650);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,25 @@
|
||||||
|
---
|
||||||
|
import { Icon } from "astro-icon/components";
|
||||||
|
import RichText from "components/RichText/RichText.astro";
|
||||||
|
import Tooltip from "components/Tooltip.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import type { RichTextContent } from "src/shared/payload/payload-sdk";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
notes: RichTextContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { notes } = Astro.props;
|
||||||
|
|
||||||
|
const { t } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
---
|
||||||
|
|
||||||
|
<Tooltip trigger="click">
|
||||||
|
<div id="tooltip-content" slot="tooltip-content">
|
||||||
|
<RichText content={notes} />
|
||||||
|
</div>
|
||||||
|
<div class="pressable-label">
|
||||||
|
<Icon name="material-symbols:comment-outline" />
|
||||||
|
<p>{t("timeline.eventFooter.note")}</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
|
@ -0,0 +1,53 @@
|
||||||
|
---
|
||||||
|
import { Icon } from "astro-icon/components";
|
||||||
|
import Tooltip from "components/Tooltip.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import type { EndpointSource } from "src/shared/payload/payload-sdk";
|
||||||
|
import { formatInlineTitle } from "src/utils/format";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
sources: EndpointSource[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { sources } = Astro.props;
|
||||||
|
|
||||||
|
const { getLocalizedUrl, getLocalizedMatch, t } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
---
|
||||||
|
|
||||||
|
<Tooltip trigger="click">
|
||||||
|
<div id="tooltip-content" slot="tooltip-content">
|
||||||
|
{
|
||||||
|
sources.map((source) =>
|
||||||
|
source.type === "url" ? (
|
||||||
|
<a class="pressable-link" href={source.url} target={"_blank"} rel={"noopener noreferrer"}>
|
||||||
|
{source.label}
|
||||||
|
</a>
|
||||||
|
) : source.type === "collectible" ? (
|
||||||
|
<a
|
||||||
|
class="pressable-link"
|
||||||
|
href={getLocalizedUrl(`/collectibles/${source.collectible.slug}`)}>
|
||||||
|
{formatInlineTitle(getLocalizedMatch(source.collectible.translations))}
|
||||||
|
</a>
|
||||||
|
) : (
|
||||||
|
<a class="pressable-link" href={getLocalizedUrl(`/pages/${source.page.slug}`)}>
|
||||||
|
{formatInlineTitle(getLocalizedMatch(source.page.translations))}
|
||||||
|
</a>
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div class="pressable-label">
|
||||||
|
<Icon name="material-symbols:edit-note" />
|
||||||
|
<p>
|
||||||
|
{t("timeline.eventFooter.sources", { count: sources.length })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#tooltip-content {
|
||||||
|
display: grid;
|
||||||
|
gap: 0.5em;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,53 @@
|
||||||
|
---
|
||||||
|
import type { EndpointChronologyEvent } from "src/shared/payload/payload-sdk";
|
||||||
|
import TimelineEvent from "./TimelineEvent.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import { dataConfig } from "src/dataConfig";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
year: number;
|
||||||
|
events: EndpointChronologyEvent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { year, events } = Astro.props;
|
||||||
|
const { formatTimelineDate } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
---
|
||||||
|
|
||||||
|
{dataConfig.timeline.yearsWithABreakBefore.includes(year) && <hr id={`hr-${year}`} />}
|
||||||
|
|
||||||
|
<h2 id={year.toString()}>
|
||||||
|
{formatTimelineDate(events.length === 1 ? events[0]!.date : { year })}
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="year-container">
|
||||||
|
{events.map((event) => <TimelineEvent event={event} displayDate={events.length > 1} />)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 3px dashed var(--color-base-500);
|
||||||
|
margin-block: 5em;
|
||||||
|
scroll-margin-block: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.year-container {
|
||||||
|
border-left: 1px solid var(--color-base-600);
|
||||||
|
padding-left: 1em;
|
||||||
|
padding-block: 1em;
|
||||||
|
margin-bottom: 3em;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
padding-bottom: 0.2em;
|
||||||
|
padding-inline: 0.2em;
|
||||||
|
color: var(--color-base-700);
|
||||||
|
border-bottom: 1px solid var(--color-base-600);
|
||||||
|
scroll-margin-block: 1em;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,110 @@
|
||||||
|
---
|
||||||
|
import { payload } from "src/shared/payload/payload-sdk";
|
||||||
|
import { groupBy } from "src/utils/array";
|
||||||
|
import TimelineYear from "./_components/TimelineYear.astro";
|
||||||
|
import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||||
|
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||||
|
import Card from "components/Card.astro";
|
||||||
|
import { getI18n } from "src/i18n/i18n";
|
||||||
|
import AppLayoutBackgroundImg from "components/AppLayout/components/AppLayoutBackgroundImg.astro";
|
||||||
|
import { dataConfig } from "src/dataConfig";
|
||||||
|
|
||||||
|
const events = await payload.getChronologyEvents();
|
||||||
|
const groupedEvents = groupBy(events, (event) => event.date.year);
|
||||||
|
const { getLocalizedUrl, t, formatTimelineDate } = await getI18n(Astro.locals.currentLocale);
|
||||||
|
---
|
||||||
|
|
||||||
|
<AppEmptyLayout>
|
||||||
|
<AppLayoutBackgroundImg
|
||||||
|
img={{
|
||||||
|
url: "/img/timeline-background.webp",
|
||||||
|
filename: "timeline-background",
|
||||||
|
width: 2478,
|
||||||
|
height: 4110,
|
||||||
|
mimeType: "image/webp",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<AppLayoutTitle title={t("timeline.title")} />
|
||||||
|
|
||||||
|
<div id="summary" class="prose">
|
||||||
|
<p>
|
||||||
|
{t("timeline.description")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-container">
|
||||||
|
<Card>
|
||||||
|
<div class="card-content prose">
|
||||||
|
<h3>{t("timeline.notes.title")}</h3>
|
||||||
|
|
||||||
|
<p
|
||||||
|
set:html={t("timeline.notes.content", {
|
||||||
|
worldInside: `<a href="${getLocalizedUrl(
|
||||||
|
"/collectibles/world-inside"
|
||||||
|
)}">World Inside</a>`,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<div class="card-content prose">
|
||||||
|
<h3>{t("timeline.priorCataclysmNote.title")}</h3>
|
||||||
|
|
||||||
|
<p
|
||||||
|
set:html={t("timeline.priorCataclysmNote.content", {
|
||||||
|
worldInside: `<a href="${getLocalizedUrl(
|
||||||
|
"/collectibles/world-inside"
|
||||||
|
)}">World Inside</a>`,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<div class="card-content prose jump-card">
|
||||||
|
<h3>{t("timeline.jumpTo")}</h3>
|
||||||
|
|
||||||
|
{
|
||||||
|
dataConfig.timeline.eras.map(({ name, start, end }) => (
|
||||||
|
<p
|
||||||
|
set:html={t(name, {
|
||||||
|
start: `<a href="#${start}">${formatTimelineDate({ year: start })}</a>`,
|
||||||
|
end: `<a href="#${end}">${formatTimelineDate({ year: end })}</a>`,
|
||||||
|
})}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
{groupedEvents.map(({ key, values }) => <TimelineYear year={key} events={values} />)}
|
||||||
|
</AppEmptyLayout>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
#summary {
|
||||||
|
backdrop-filter: blur(5px);
|
||||||
|
padding: 1.5em;
|
||||||
|
margin: -1.5em;
|
||||||
|
margin-block: 1em;
|
||||||
|
border-radius: 3em;
|
||||||
|
width: fit-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
padding: clamp(1em, 4vw, 3em);
|
||||||
|
max-width: 35rem;
|
||||||
|
|
||||||
|
&.jump-card > p {
|
||||||
|
margin-block: 0.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-container {
|
||||||
|
margin-top: 2em;
|
||||||
|
margin-bottom: 4em;
|
||||||
|
display: flex;
|
||||||
|
gap: 2em;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -20,8 +20,8 @@ export type RecorderBiographies =
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -46,26 +46,25 @@ export interface Config {
|
||||||
pages: Page;
|
pages: Page;
|
||||||
collectibles: Collectible;
|
collectibles: Collectible;
|
||||||
folders: Folder;
|
folders: Folder;
|
||||||
'chronology-items': ChronologyItem;
|
"chronology-events": ChronologyEvent;
|
||||||
'chronology-eras': ChronologyEra;
|
|
||||||
notes: Note;
|
notes: Note;
|
||||||
images: Image;
|
images: Image;
|
||||||
'background-images': BackgroundImage;
|
"background-images": BackgroundImage;
|
||||||
'recorders-thumbnails': RecordersThumbnail;
|
"recorders-thumbnails": RecordersThumbnail;
|
||||||
videos: Video;
|
videos: Video;
|
||||||
'videos-channels': VideosChannel;
|
"videos-channels": VideosChannel;
|
||||||
tags: Tag;
|
tags: Tag;
|
||||||
'tags-groups': TagsGroup;
|
"tags-groups": TagsGroup;
|
||||||
recorders: Recorder;
|
recorders: Recorder;
|
||||||
languages: Language;
|
languages: Language;
|
||||||
currencies: Currency;
|
currencies: Currency;
|
||||||
wordings: Wording;
|
wordings: Wording;
|
||||||
'generic-contents': GenericContent;
|
"generic-contents": GenericContent;
|
||||||
'payload-preferences': PayloadPreference;
|
"payload-preferences": PayloadPreference;
|
||||||
'payload-migrations': PayloadMigration;
|
"payload-migrations": PayloadMigration;
|
||||||
};
|
};
|
||||||
globals: {
|
globals: {
|
||||||
'home-folders': HomeFolder;
|
"home-folders": HomeFolder;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -75,7 +74,7 @@ export interface Config {
|
||||||
export interface Page {
|
export interface Page {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
type: 'Content' | 'Post' | 'Generic';
|
type: "Content" | "Post" | "Generic";
|
||||||
thumbnail?: string | Image | null;
|
thumbnail?: string | Image | null;
|
||||||
backgroundImage?: string | BackgroundImage | null;
|
backgroundImage?: string | BackgroundImage | null;
|
||||||
tags?: (string | Tag)[] | null;
|
tags?: (string | Tag)[] | null;
|
||||||
|
@ -93,8 +92,8 @@ export interface Page {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -108,8 +107,8 @@ export interface Page {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -126,7 +125,7 @@ export interface Page {
|
||||||
updatedBy: string | Recorder;
|
updatedBy: string | Recorder;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: ('draft' | 'published') | null;
|
_status?: ("draft" | "published") | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -237,7 +236,7 @@ export interface Recorder {
|
||||||
avatar?: string | RecordersThumbnail | null;
|
avatar?: string | RecordersThumbnail | null;
|
||||||
languages?: (string | Language)[] | null;
|
languages?: (string | Language)[] | null;
|
||||||
biographies?: RecorderBiographies;
|
biographies?: RecorderBiographies;
|
||||||
role?: ('Admin' | 'Recorder' | 'Api')[] | null;
|
role?: ("Admin" | "Recorder" | "Api")[] | null;
|
||||||
anonymize: boolean;
|
anonymize: boolean;
|
||||||
email: string;
|
email: string;
|
||||||
resetPasswordToken?: string | null;
|
resetPasswordToken?: string | null;
|
||||||
|
@ -301,8 +300,8 @@ export interface Folder {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -327,11 +326,11 @@ export interface Folder {
|
||||||
files?:
|
files?:
|
||||||
| (
|
| (
|
||||||
| {
|
| {
|
||||||
relationTo: 'collectibles';
|
relationTo: "collectibles";
|
||||||
value: string | Collectible;
|
value: string | Collectible;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'pages';
|
relationTo: "pages";
|
||||||
value: string | Page;
|
value: string | Page;
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
|
@ -347,7 +346,7 @@ export interface Collectible {
|
||||||
id: string;
|
id: string;
|
||||||
slug: string;
|
slug: string;
|
||||||
thumbnail?: string | Image | null;
|
thumbnail?: string | Image | null;
|
||||||
nature: 'Physical' | 'Digital';
|
nature: "Physical" | "Digital";
|
||||||
languages?: (string | Language)[] | null;
|
languages?: (string | Language)[] | null;
|
||||||
tags?: (string | Tag)[] | null;
|
tags?: (string | Tag)[] | null;
|
||||||
translations: {
|
translations: {
|
||||||
|
@ -362,8 +361,8 @@ export interface Collectible {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -455,8 +454,8 @@ export interface Collectible {
|
||||||
pageInfoEnabled?: boolean | null;
|
pageInfoEnabled?: boolean | null;
|
||||||
pageInfo?: {
|
pageInfo?: {
|
||||||
pageCount: number;
|
pageCount: number;
|
||||||
bindingType?: ('Paperback' | 'Hardcover') | null;
|
bindingType?: ("Paperback" | "Hardcover") | null;
|
||||||
pageOrder?: ('Left to right' | 'Right to left') | null;
|
pageOrder?: ("Left to right" | "Right to left") | null;
|
||||||
};
|
};
|
||||||
folders?: (string | Folder)[] | null;
|
folders?: (string | Folder)[] | null;
|
||||||
parentItems?: (string | Collectible)[] | null;
|
parentItems?: (string | Collectible)[] | null;
|
||||||
|
@ -465,11 +464,11 @@ export interface Collectible {
|
||||||
| {
|
| {
|
||||||
content:
|
content:
|
||||||
| {
|
| {
|
||||||
relationTo: 'pages';
|
relationTo: "pages";
|
||||||
value: string | Page;
|
value: string | Page;
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
relationTo: 'generic-contents';
|
relationTo: "generic-contents";
|
||||||
value: string | GenericContent;
|
value: string | GenericContent;
|
||||||
};
|
};
|
||||||
range?:
|
range?:
|
||||||
|
@ -479,14 +478,14 @@ export interface Collectible {
|
||||||
end: number;
|
end: number;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'pageRange';
|
blockType: "pageRange";
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
start: string;
|
start: string;
|
||||||
end: string;
|
end: string;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'timeRange';
|
blockType: "timeRange";
|
||||||
}
|
}
|
||||||
| {
|
| {
|
||||||
translations?:
|
translations?:
|
||||||
|
@ -499,8 +498,8 @@ export interface Collectible {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -512,7 +511,7 @@ export interface Collectible {
|
||||||
| null;
|
| null;
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'other';
|
blockType: "other";
|
||||||
}
|
}
|
||||||
)[]
|
)[]
|
||||||
| null;
|
| null;
|
||||||
|
@ -522,7 +521,7 @@ export interface Collectible {
|
||||||
updatedBy: string | Recorder;
|
updatedBy: string | Recorder;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: ('draft' | 'published') | null;
|
_status?: ("draft" | "published") | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -548,9 +547,9 @@ export interface GenericContent {
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "chronology-items".
|
* via the `definition` "chronology-events".
|
||||||
*/
|
*/
|
||||||
export interface ChronologyItem {
|
export interface ChronologyEvent {
|
||||||
id: string;
|
id: string;
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
date: {
|
date: {
|
||||||
|
@ -559,6 +558,7 @@ export interface ChronologyItem {
|
||||||
day?: number | null;
|
day?: number | null;
|
||||||
};
|
};
|
||||||
events: {
|
events: {
|
||||||
|
sources?: (UrlBlock | CollectibleBlock | PageBlock)[] | null;
|
||||||
translations: {
|
translations: {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
sourceLanguage: string | Language;
|
sourceLanguage: string | Language;
|
||||||
|
@ -570,8 +570,8 @@ export interface ChronologyItem {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -585,8 +585,8 @@ export interface ChronologyItem {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -603,42 +603,63 @@ export interface ChronologyItem {
|
||||||
updatedBy: string | Recorder;
|
updatedBy: string | Recorder;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
_status?: ('draft' | 'published') | null;
|
_status?: ("draft" | "published") | null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "chronology-eras".
|
* via the `definition` "UrlBlock".
|
||||||
*/
|
*/
|
||||||
export interface ChronologyEra {
|
export interface UrlBlock {
|
||||||
id: string;
|
url: string;
|
||||||
slug: string;
|
|
||||||
startingYear: number;
|
|
||||||
endingYear: number;
|
|
||||||
translations?:
|
|
||||||
| {
|
|
||||||
language: string | Language;
|
|
||||||
title: string;
|
|
||||||
description?: {
|
|
||||||
root: {
|
|
||||||
children: {
|
|
||||||
type: string;
|
|
||||||
version: number;
|
|
||||||
[k: string]: unknown;
|
|
||||||
}[];
|
|
||||||
direction: ('ltr' | 'rtl') | null;
|
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
|
||||||
indent: number;
|
|
||||||
type: string;
|
|
||||||
version: number;
|
|
||||||
};
|
|
||||||
[k: string]: unknown;
|
|
||||||
} | null;
|
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
}[]
|
blockName?: string | null;
|
||||||
|
blockType: "urlBlock";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "CollectibleBlock".
|
||||||
|
*/
|
||||||
|
export interface CollectibleBlock {
|
||||||
|
collectible: string | Collectible;
|
||||||
|
range?:
|
||||||
|
| (
|
||||||
|
| {
|
||||||
|
page: number;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: "page";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
timestamp: string;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: "timestamp";
|
||||||
|
}
|
||||||
|
| {
|
||||||
|
translations: {
|
||||||
|
language: string | Language;
|
||||||
|
note: string;
|
||||||
|
id?: string | null;
|
||||||
|
}[];
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: "other";
|
||||||
|
}
|
||||||
|
)[]
|
||||||
| null;
|
| null;
|
||||||
events?: (string | ChronologyItem)[] | null;
|
id?: string | null;
|
||||||
updatedAt: string;
|
blockName?: string | null;
|
||||||
createdAt: string;
|
blockType: "collectibleBlock";
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "PageBlock".
|
||||||
|
*/
|
||||||
|
export interface PageBlock {
|
||||||
|
page: string | Page;
|
||||||
|
id?: string | null;
|
||||||
|
blockName?: string | null;
|
||||||
|
blockType: "pageBlock";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -653,8 +674,8 @@ export interface Note {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -672,7 +693,7 @@ export interface Video {
|
||||||
id: string;
|
id: string;
|
||||||
uid: string;
|
uid: string;
|
||||||
gone: boolean;
|
gone: boolean;
|
||||||
source: 'YouTube' | 'NicoNico' | 'Tumblr';
|
source: "YouTube" | "NicoNico" | "Tumblr";
|
||||||
title: string;
|
title: string;
|
||||||
description?: string | null;
|
description?: string | null;
|
||||||
likes?: number | null;
|
likes?: number | null;
|
||||||
|
@ -708,7 +729,7 @@ export interface Wording {
|
||||||
export interface PayloadPreference {
|
export interface PayloadPreference {
|
||||||
id: string;
|
id: string;
|
||||||
user: {
|
user: {
|
||||||
relationTo: 'recorders';
|
relationTo: "recorders";
|
||||||
value: string | Recorder;
|
value: string | Recorder;
|
||||||
};
|
};
|
||||||
key?: string | null;
|
key?: string | null;
|
||||||
|
@ -764,15 +785,15 @@ export interface LineBlock {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
};
|
||||||
blockType: 'lineBlock';
|
blockType: "lineBlock";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -786,15 +807,15 @@ export interface CueBlock {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
};
|
};
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
};
|
};
|
||||||
blockType: 'cueBlock';
|
blockType: "cueBlock";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -804,17 +825,17 @@ export interface TranscriptBlock {
|
||||||
lines: (LineBlock | CueBlock)[];
|
lines: (LineBlock | CueBlock)[];
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'transcriptBlock';
|
blockType: "transcriptBlock";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "BreakBlock".
|
* via the `definition` "BreakBlock".
|
||||||
*/
|
*/
|
||||||
export interface BreakBlock {
|
export interface BreakBlock {
|
||||||
type: 'Scene break' | 'Empty space' | 'Solid line' | 'Dotted line';
|
type: "Scene break" | "Empty space" | "Solid line" | "Dotted line";
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'breakBlock';
|
blockType: "breakBlock";
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
@ -828,8 +849,8 @@ export interface SectionBlock {
|
||||||
version: number;
|
version: number;
|
||||||
[k: string]: unknown;
|
[k: string]: unknown;
|
||||||
}[];
|
}[];
|
||||||
direction: ('ltr' | 'rtl') | null;
|
direction: ("ltr" | "rtl") | null;
|
||||||
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
|
format: "left" | "start" | "center" | "right" | "end" | "justify" | "";
|
||||||
indent: number;
|
indent: number;
|
||||||
type: string;
|
type: string;
|
||||||
version: number;
|
version: number;
|
||||||
|
@ -838,17 +859,18 @@ export interface SectionBlock {
|
||||||
};
|
};
|
||||||
id?: string | null;
|
id?: string | null;
|
||||||
blockName?: string | null;
|
blockName?: string | null;
|
||||||
blockType: 'sectionBlock';
|
blockType: "sectionBlock";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "payload" {
|
||||||
|
export interface GeneratedTypes extends Config {}
|
||||||
|
}
|
||||||
|
|
||||||
/////////////// CONSTANTS ///////////////
|
/////////////// CONSTANTS ///////////////
|
||||||
|
|
||||||
|
|
||||||
export enum Collections {
|
export enum Collections {
|
||||||
ChronologyEras = "chronology-eras",
|
ChronologyEvents = "chronology-events",
|
||||||
ChronologyItems = "chronology-items",
|
|
||||||
Currencies = "currencies",
|
Currencies = "currencies",
|
||||||
Files = "files",
|
Files = "files",
|
||||||
Languages = "languages",
|
Languages = "languages",
|
||||||
|
@ -1334,7 +1356,6 @@ export type EndpointPagePreview = {
|
||||||
title: string;
|
title: string;
|
||||||
subtitle?: string;
|
subtitle?: string;
|
||||||
}[];
|
}[];
|
||||||
status: "draft" | "published";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EndpointPage = EndpointPagePreview & {
|
export type EndpointPage = EndpointPagePreview & {
|
||||||
|
@ -1368,7 +1389,6 @@ export type EndpointCollectiblePreview = {
|
||||||
description?: RichTextContent;
|
description?: RichTextContent;
|
||||||
}[];
|
}[];
|
||||||
tagGroups: EndpointTagsGroup[];
|
tagGroups: EndpointTagsGroup[];
|
||||||
status: "draft" | "published";
|
|
||||||
releaseDate?: string;
|
releaseDate?: string;
|
||||||
languages: string[];
|
languages: string[];
|
||||||
};
|
};
|
||||||
|
@ -1441,6 +1461,40 @@ export type TableOfContentEntry = {
|
||||||
children: TableOfContentEntry[];
|
children: TableOfContentEntry[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type EndpointChronologyEvent = {
|
||||||
|
id: string;
|
||||||
|
date: {
|
||||||
|
year: number;
|
||||||
|
month?: number;
|
||||||
|
day?: number;
|
||||||
|
};
|
||||||
|
events: {
|
||||||
|
sources: EndpointSource[];
|
||||||
|
translations: {
|
||||||
|
language: string;
|
||||||
|
sourceLanguage: string;
|
||||||
|
title?: string;
|
||||||
|
description?: RichTextContent;
|
||||||
|
notes?: RichTextContent;
|
||||||
|
transcribers: EndpointRecorder[];
|
||||||
|
translators: EndpointRecorder[];
|
||||||
|
proofreaders: EndpointRecorder[];
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EndpointSource =
|
||||||
|
| { type: "url"; url: string; label: string }
|
||||||
|
| {
|
||||||
|
type: "collectible";
|
||||||
|
collectible: EndpointCollectiblePreview;
|
||||||
|
range?:
|
||||||
|
| { type: "page"; page: number }
|
||||||
|
| { type: "timestamp"; timestamp: string }
|
||||||
|
| { type: "custom"; translations: { language: string; note: string }[] };
|
||||||
|
}
|
||||||
|
| { type: "page"; page: EndpointPagePreview };
|
||||||
|
|
||||||
export type PayloadImage = {
|
export type PayloadImage = {
|
||||||
url: string;
|
url: string;
|
||||||
width: number;
|
width: number;
|
||||||
|
@ -1450,8 +1504,6 @@ export type PayloadImage = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const payload = {
|
export const payload = {
|
||||||
getEras: async (): Promise<EndpointEra[]> =>
|
|
||||||
await (await request(payloadApiUrl(Collections.ChronologyEras, `all`))).json(),
|
|
||||||
getHomeFolders: async (): Promise<EndpointHomeFolder[]> =>
|
getHomeFolders: async (): Promise<EndpointHomeFolder[]> =>
|
||||||
await (await request(payloadApiUrl(Collections.HomeFolders, `all`, true))).json(),
|
await (await request(payloadApiUrl(Collections.HomeFolders, `all`, true))).json(),
|
||||||
getFolder: async (slug: string): Promise<EndpointFolder> =>
|
getFolder: async (slug: string): Promise<EndpointFolder> =>
|
||||||
|
@ -1468,4 +1520,8 @@ export const payload = {
|
||||||
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(),
|
await (await request(payloadApiUrl(Collections.Pages, `slug/${slug}`))).json(),
|
||||||
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
|
getCollectible: async (slug: string): Promise<EndpointCollectible> =>
|
||||||
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(),
|
await (await request(payloadApiUrl(Collections.Collectibles, `slug/${slug}`))).json(),
|
||||||
|
getChronologyEvents: async (): Promise<EndpointChronologyEvent[]> =>
|
||||||
|
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(),
|
||||||
|
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
|
||||||
|
await (await request(payloadApiUrl(Collections.ChronologyEvents, id))).json(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
export const groupBy = <K, T>(array: T[], getKey: (item: T) => K): { key: K; values: T[] }[] => {
|
||||||
|
const map = new Map<K, T[]>();
|
||||||
|
array.forEach((item) => {
|
||||||
|
const key = getKey(item);
|
||||||
|
const currentValueInMap = map.get(key) ?? [];
|
||||||
|
currentValueInMap.push(item);
|
||||||
|
map.set(key, currentValueInMap);
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...map.entries()].map(([key, values]) => ({ key, values }));
|
||||||
|
};
|
|
@ -47,3 +47,9 @@ export const formatRichTextToString = (content: RichTextContent): string => {
|
||||||
|
|
||||||
return content.root.children.map(formatNode).join("\n\n");
|
return content.root.children.map(formatNode).join("\n\n");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const capitalize = (string: string): string => {
|
||||||
|
const [firstLetter, ...otherLetters] = string;
|
||||||
|
if (firstLetter === undefined) return "";
|
||||||
|
return [firstLetter.toUpperCase(), ...otherLetters].join("");
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue