Added media support in RTC

This commit is contained in:
DrMint 2024-04-07 02:19:50 +02:00
parent 9a1cbd28f7
commit 35e2991086
18 changed files with 357 additions and 161 deletions

View File

@ -4,6 +4,9 @@
- [Timeline] inline links to pages not working (timeline 2026/06) - [Timeline] inline links to pages not working (timeline 2026/06)
- Save cookies for longer than just the session - Save cookies for longer than just the session
- [Image] media page
- [Video] media page
- [Audio] media page
## Mid term ## Mid term

View File

@ -5,7 +5,6 @@ import Topbar from "./components/Topbar/Topbar.astro";
import Footer from "./components/Footer.astro"; import Footer from "./components/Footer.astro";
import AppLayoutTitle from "./components/AppLayoutTitle.astro"; import AppLayoutTitle from "./components/AppLayoutTitle.astro";
import type { ComponentProps } from "astro/types"; import type { ComponentProps } from "astro/types";
import type { PayloadImage } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
openGraph?: ComponentProps<typeof Html>["openGraph"]; openGraph?: ComponentProps<typeof Html>["openGraph"];
@ -17,7 +16,7 @@ interface Props {
illustration?: string; illustration?: string;
illustrationSize?: string; illustrationSize?: string;
illustrationPosition?: string; illustrationPosition?: string;
backgroundImage?: PayloadImage | undefined; backgroundImage?: ComponentProps<typeof AppLayoutBackgroundImg>["img"] | undefined;
hideFooterLinks?: boolean; hideFooterLinks?: boolean;
hideHomeButton?: boolean; hideHomeButton?: boolean;
} }

View File

@ -3,7 +3,7 @@ import type { PayloadImage } from "src/shared/payload/payload-sdk";
import { getRandomId } from "src/utils/random"; import { getRandomId } from "src/utils/random";
interface Props { interface Props {
img: PayloadImage; img: Pick<PayloadImage, "url" | "width" | "height">;
} }
const { const {

View File

@ -50,23 +50,14 @@ const { currentTheme } = Astro.locals;
"light-theme": currentTheme === "light", "light-theme": currentTheme === "light",
"dark-theme": currentTheme === "dark", "dark-theme": currentTheme === "dark",
"texture-dots": !isIOS, "texture-dots": !isIOS,
}} }}>
>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="icon" type="image/x-icon" href="/favicon.ico" /> <link rel="icon" type="image/x-icon" href="/favicon.ico" />
<link rel="stylesheet" href="/css/tippy.css" /> <link rel="stylesheet" href="/css/tippy.css" />
<meta <meta name="theme-color" media="(prefers-color-scheme: light)" content="#fdebd4" />
name="theme-color" <meta name="theme-color" media="(prefers-color-scheme: dark)" content="#27231e" />
media="(prefers-color-scheme: light)"
content="#fdebd4"
/>
<meta
name="theme-color"
media="(prefers-color-scheme: dark)"
content="#27231e"
/>
<link rel="manifest" href="/site.webmanifest" /> <link rel="manifest" href="/site.webmanifest" />
{/* Meta & OpenGraph */} {/* Meta & OpenGraph */}
@ -80,10 +71,7 @@ const { currentTheme } = Astro.locals;
<meta name="twitter:card" content="summary_large_image" /> <meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:image" content={thumbnail} /> <meta name="twitter:image" content={thumbnail} />
<meta <meta property="og:type" content={video ? "video.movie" : audio ? "music.song" : "website"} />
property="og:type"
content={video ? "video.movie" : audio ? "music.song" : "website"}
/>
<meta property="og:locale" content={currentLocale} /> <meta property="og:locale" content={currentLocale} />
<meta property="og:site_name" content={t("global.siteName")} /> <meta property="og:site_name" content={t("global.siteName")} />
@ -133,9 +121,7 @@ const { currentTheme } = Astro.locals;
<AppLayoutSpinner /> <AppLayoutSpinner />
</html> </html>
{ {/* ------------------------------------------- CSS -------------------------------------------- */}
/* ------------------------------------------- CSS -------------------------------------------- */
}
<style is:global> <style is:global>
html { html {
@ -467,8 +453,7 @@ const { currentTheme } = Astro.locals;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
transition-duration: 250ms; transition-duration: 250ms;
transition-property: padding-top, box-shadow, background-color, color, transition-property: padding-top, box-shadow, background-color, color, border-color;
border-color;
&:hover { &:hover {
--foreground-color: var(--color-base-1000); --foreground-color: var(--color-base-1000);
@ -587,19 +572,13 @@ const { currentTheme } = Astro.locals;
} }
</style> </style>
{ {/* ------------------------------------------- JS --------------------------------------------- */}
/* ------------------------------------------- JS --------------------------------------------- */
}
<script is:inline> <script is:inline>
Array.from(document.querySelectorAll(".when-no-js")).forEach((node) => Array.from(document.querySelectorAll(".when-no-js")).forEach((node) => node.remove());
node.remove()
);
document.addEventListener("astro:before-swap", ({ newDocument }) => { document.addEventListener("astro:before-swap", ({ newDocument }) => {
Array.from(newDocument.querySelectorAll(".when-no-js")).forEach((node) => Array.from(newDocument.querySelectorAll(".when-no-js")).forEach((node) => node.remove());
node.remove()
);
}); });
</script> </script>

View File

@ -0,0 +1,53 @@
---
interface Props {
id?: string;
header: number;
}
const { header, id } = Astro.props;
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
{
header === 1 ? (
<h1 id={id}>
<slot />
</h1>
) : header === 2 ? (
<h2 id={id}>
<slot />
</h2>
) : header === 3 ? (
<h3 id={id}>
<slot />
</h3>
) : header === 4 ? (
<h4 id={id}>
<slot />
</h4>
) : header === 5 ? (
<h5 id={id}>
<slot />
</h5>
) : (
<h6 id={id}>
<slot />
</h6>
)
}
{/* ------------------------------------------- CSS -------------------------------------------- */}
<style>
h1,
h2,
h3,
h4,
h5,
h6 {
display: inline-flex;
place-items: center;
gap: 0.5em;
}
</style>

View File

@ -1,10 +1,10 @@
--- ---
import GenericPreview from "components/Previews/GenericPreview.astro"; import GenericPreview from "components/Previews/GenericPreview.astro";
import { getI18n } from "src/i18n/i18n"; import { getI18n } from "src/i18n/i18n";
import type { EndpointCollectiblePreview } from "src/shared/payload/payload-sdk"; import type { EndpointCollectible } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
collectible: EndpointCollectiblePreview; collectible: EndpointCollectible;
} }
const { getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale); const { getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);

View File

@ -1,10 +1,10 @@
--- ---
import GenericPreview from "components/Previews/GenericPreview.astro"; import GenericPreview from "components/Previews/GenericPreview.astro";
import { getI18n } from "src/i18n/i18n"; import { getI18n } from "src/i18n/i18n";
import type { EndpointPagePreview } from "src/shared/payload/payload-sdk"; import type { EndpointPage } from "src/shared/payload/payload-sdk";
interface Props { interface Props {
page: EndpointPagePreview; page: EndpointPage;
} }
const { getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale); const { getLocalizedMatch, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);

View File

@ -1,4 +1,5 @@
--- ---
import HeaderTitle from "components/HeaderTitle.astro";
import RichText from "components/RichText/RichText.astro"; import RichText from "components/RichText/RichText.astro";
import type { RichTextSectionBlock } from "src/shared/payload/payload-sdk"; import type { RichTextSectionBlock } from "src/shared/payload/payload-sdk";
import type { RichTextContext } from "src/utils/richText"; import type { RichTextContext } from "src/utils/richText";
@ -13,34 +14,10 @@ const { node, context } = Astro.props;
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
{ <HeaderTitle id={node.anchorHash} header={context.depth + 1}>
context.depth < 2 ? ( <span>{`${node.anchorHash} `}</span>
<h2 id={node.anchorHash}> {node.fields.blockName}
<span>{`${node.anchorHash} `}</span> </HeaderTitle>
{node.fields.blockName}
</h2>
) : context.depth === 2 ? (
<h3 id={node.anchorHash}>
<span>{`${node.anchorHash} `}</span>
{node.fields.blockName}
</h3>
) : context.depth === 3 ? (
<h4 id={node.anchorHash}>
<span>{`${node.anchorHash} `}</span>
{node.fields.blockName}
</h4>
) : context.depth === 4 ? (
<h5 id={node.anchorHash}>
<span>{`${node.anchorHash} `}</span>
{node.fields.blockName}
</h5>
) : (
<h6 id={node.anchorHash}>
<span>{`${node.anchorHash} `}</span>
{node.fields.blockName}
</h6>
)
}
<RichText content={node.fields.content} context={{ ...context, depth: context.depth + 1 }} /> <RichText content={node.fields.content} context={{ ...context, depth: context.depth + 1 }} />
@ -51,6 +28,6 @@ const { node, context } = Astro.props;
color: var(--color-base-650); color: var(--color-base-650);
font-weight: 500; font-weight: 500;
font-size: 70%; font-size: 70%;
margin-right: 0.3em; place-self: end;
} }
</style> </style>

View File

@ -19,9 +19,7 @@ interface Props {
const { node, context } = Astro.props; const { node, context } = Astro.props;
--- ---
{ {/* ------------------------------------------- HTML ------------------------------------------- */}
/* ------------------------------------------- HTML ------------------------------------------- */
}
{ {
isUploadNodeImageNode(node) ? ( isUploadNodeImageNode(node) ? (

View File

@ -0,0 +1,38 @@
---
import { Icon } from "astro-icon/components";
import { getI18n } from "src/i18n/i18n";
interface Props {
url: string;
}
const { url } = Astro.props;
const { t } = await getI18n(Astro.locals.currentLocale);
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
<div class="button">
<a href={url}>
<div class="pressable-label">
<Icon name="material-symbols:left-click" />
<p>{t("global.openMediaPage")}</p>
</div>
</a>
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */}
<style>
.button {
display: flex;
place-items: start;
gap: 0.3em;
font-size: 85%;
& > a > div {
padding: 0.3em 0.6em;
padding-right: 0.8em;
}
}
</style>

View File

@ -1,32 +1,54 @@
--- ---
import { type RichTextUploadAudioNode } from "src/shared/payload/payload-sdk"; import { type RichTextUploadAudioNode } from "src/shared/payload/payload-sdk";
import type { RichTextContext } from "src/utils/richText"; import type { RichTextContext } from "src/utils/richText";
import OpenMediaPageButton from "./OpenMediaPageButton.astro";
import { getI18n } from "src/i18n/i18n";
import HeaderTitle from "components/HeaderTitle.astro";
import { Icon } from "astro-icon/components";
interface Props { interface Props {
node: RichTextUploadAudioNode; node: RichTextUploadAudioNode;
context: RichTextContext; context: RichTextContext;
} }
const { node } = Astro.props; const {
node: {
value: { id, url, mimeType, translations },
},
context,
} = Astro.props;
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
const { title } = getLocalizedMatch(translations);
--- ---
{ {/* ------------------------------------------- HTML ------------------------------------------- */}
/* ------------------------------------------- HTML ------------------------------------------- */
}
<audio controls> <div>
<source src={node.value.url} type={node.value.mimeType} /> <HeaderTitle header={context.depth + 2}>
</audio> <Icon name="material-symbols:headphones" />
{title}
</HeaderTitle>
{ <audio controls>
/* ------------------------------------------- CSS -------------------------------------------- */ <source src={url} type={mimeType} />
} </audio>
<OpenMediaPageButton url={`/audios/${id}`} />
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */}
<style> <style>
audio { div {
width: 100%; margin-block: 4em;
margin-block: 3em;
border-radius: 16px; & > audio {
box-shadow: 0 5px 20px -10px var(--color-shadow); margin-top: 0.4em;
margin-bottom: 0.2em;
width: 100%;
border-radius: 16px;
box-shadow: 0 5px 20px -10px var(--color-shadow);
}
} }
</style> </style>

View File

@ -1,27 +1,60 @@
--- ---
import { type RichTextUploadImageNode } from "src/shared/payload/payload-sdk"; import { type RichTextUploadImageNode } from "src/shared/payload/payload-sdk";
import type { RichTextContext } from "src/utils/richText"; import type { RichTextContext } from "src/utils/richText";
import OpenMediaPageButton from "./OpenMediaPageButton.astro";
import { getI18n } from "src/i18n/i18n";
import HeaderTitle from "components/HeaderTitle.astro";
import { Icon } from "astro-icon/components";
interface Props { interface Props {
node: RichTextUploadImageNode; node: RichTextUploadImageNode;
context: RichTextContext; context: RichTextContext;
} }
const { node } = Astro.props; const {
node: {
value: { id, url, translations },
},
context,
} = Astro.props;
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
const { title }: { title?: string } =
translations.length > 0 ? getLocalizedMatch(translations) : {};
const mediaPage = `/images/${id}`;
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<img src={node.value.url} /> <div>
{
title && (
<HeaderTitle header={context.depth + 2}>
<Icon name="material-symbols:image-outline" />
{title}
</HeaderTitle>
)
}
<a href={mediaPage}><img src={url} /></a>
<OpenMediaPageButton url={mediaPage} />
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
<style> <style>
img { div {
width: 100%; margin-block: 4em;
height: auto;
margin-block: 3em; & > a > img {
border-radius: 16px; width: 100%;
box-shadow: 0 5px 20px -10px var(--color-shadow); height: auto;
border-radius: 16px;
box-shadow: 0 5px 20px -10px var(--color-shadow);
margin-top: 0.4em;
margin-bottom: 0.2em;
}
} }
</style> </style>

View File

@ -1,29 +1,61 @@
--- ---
import { getI18n } from "src/i18n/i18n";
import { type RichTextUploadVideoNode } from "src/shared/payload/payload-sdk"; import { type RichTextUploadVideoNode } from "src/shared/payload/payload-sdk";
import { formatLocale } from "src/utils/format";
import type { RichTextContext } from "src/utils/richText"; import type { RichTextContext } from "src/utils/richText";
import OpenMediaPageButton from "./OpenMediaPageButton.astro";
import HeaderTitle from "components/HeaderTitle.astro";
import { Icon } from "astro-icon/components";
interface Props { interface Props {
node: RichTextUploadVideoNode; node: RichTextUploadVideoNode;
context: RichTextContext; context: RichTextContext;
} }
const { node } = Astro.props; const {
node: {
value: { id, url, thumbnail, mimeType, subtitles, translations },
},
context,
} = Astro.props;
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
const { title } = getLocalizedMatch(translations);
--- ---
{/* ------------------------------------------- HTML ------------------------------------------- */} {/* ------------------------------------------- HTML ------------------------------------------- */}
<video controls> <div>
<source src={node.value.url} type={node.value.mimeType} /> <HeaderTitle header={context.depth + 2}>
</video> <Icon name="material-symbols:movie-outline" />
{title}
</HeaderTitle>
<video controls poster={thumbnail?.url}>
<source src={url} type={mimeType} />
{
subtitles.map(({ language, url }) => (
<track label={formatLocale(language)} src={url} kind="subtitles" srclang={language} />
))
}
</video>
<OpenMediaPageButton url={`/videos/${id}`} />
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */} {/* ------------------------------------------- CSS -------------------------------------------- */}
<style> <style>
video { div {
width: 100%; margin-block: 4em;
height: auto;
margin-block: 3em; & > video {
border-radius: 16px; margin-top: 0.4em;
box-shadow: 0 5px 20px -10px var(--color-shadow); margin-bottom: 0.2em;
width: 100%;
height: auto;
border-radius: 16px;
box-shadow: 0 5px 20px -10px var(--color-shadow);
}
} }
</style> </style>

View File

@ -111,4 +111,5 @@ export type WordingKey =
| "global.sources.typeLabel.folder" | "global.sources.typeLabel.folder"
| "global.sources.typeLabel.collectible.range.page" | "global.sources.typeLabel.collectible.range.page"
| "global.sources.typeLabel.collectible.range.timestamp" | "global.sources.typeLabel.collectible.range.timestamp"
| "global.sources.typeLabel.collectible.range.custom"; | "global.sources.typeLabel.collectible.range.custom"
| "global.openMediaPage";

View File

@ -1,11 +1,11 @@
--- ---
import type { EndpointFolderPreview } from "src/shared/payload/payload-sdk"; import type { EndpointFolder } from "src/shared/payload/payload-sdk";
import FolderCard from "./FolderCard.astro"; import FolderCard from "./FolderCard.astro";
import { getI18n } from "src/i18n/i18n"; import { getI18n } from "src/i18n/i18n";
interface Props { interface Props {
title?: string | undefined; title?: string | undefined;
folders: EndpointFolderPreview[]; folders: EndpointFolder[];
} }
const { title, folders } = Astro.props; const { title, folders } = Astro.props;

View File

@ -17,10 +17,8 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
openGraph={{ title: t("home.title") }} openGraph={{ title: t("home.title") }}
backgroundImage={{ backgroundImage={{
url: "/img/background-image.webp", url: "/img/background-image.webp",
filename: "background-image",
height: 2279, height: 2279,
width: 1920, width: 1920,
mimeType: "image/webp",
}} }}
hideFooterLinks hideFooterLinks
hideHomeButton> hideHomeButton>

View File

@ -21,10 +21,8 @@ const { getLocalizedUrl, t, formatTimelineDate } = await getI18n(Astro.locals.cu
<AppLayoutBackgroundImg <AppLayoutBackgroundImg
img={{ img={{
url: "/img/timeline-background.webp", url: "/img/timeline-background.webp",
filename: "timeline-background",
width: 2478, width: 2478,
height: 4110, height: 4110,
mimeType: "image/webp",
}} }}
/> />
<AppLayoutTitle title={t("timeline.title")} /> <AppLayoutTitle title={t("timeline.title")} />

View File

@ -551,7 +551,6 @@ export interface Audio {
}; };
[k: string]: unknown; [k: string]: unknown;
} | null; } | null;
subfile?: string | VideoSubtitle | null;
id?: string | null; id?: string | null;
}[]; }[];
tags?: (string | Tag)[] | null; tags?: (string | Tag)[] | null;
@ -597,19 +596,6 @@ export interface MediaThumbnail {
}; };
}; };
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos-subtitles".
*/
export interface VideoSubtitle {
id: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos". * via the `definition` "videos".
@ -617,7 +603,7 @@ export interface VideoSubtitle {
export interface Video { export interface Video {
id: string; id: string;
duration: number; duration: number;
thumbnail: string | MediaThumbnail; thumbnail?: string | MediaThumbnail | null;
translations: { translations: {
language: string | Language; language: string | Language;
title: string; title: string;
@ -658,6 +644,19 @@ export interface Video {
width?: number | null; width?: number | null;
height?: number | null; height?: number | null;
} }
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos-subtitles".
*/
export interface VideoSubtitle {
id: string;
url?: string | null;
filename?: string | null;
mimeType?: string | null;
filesize?: number | null;
width?: number | null;
height?: number | null;
}
/** /**
* This interface was referenced by `Config`'s JSON-Schema * This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "videos-channels". * via the `definition` "videos-channels".
@ -666,7 +665,7 @@ export interface VideosChannel {
id: string; id: string;
url: string; url: string;
title: string; title: string;
subscribers?: number | null; subscribers: number;
videos?: (string | Video)[] | null; videos?: (string | Video)[] | null;
} }
/** /**
@ -1099,17 +1098,17 @@ export interface RichTextUploadNode extends RichTextNode {
export interface RichTextUploadImageNode extends RichTextUploadNode { export interface RichTextUploadImageNode extends RichTextUploadNode {
relationTo: Collections.Images; relationTo: Collections.Images;
value: Image; value: EndpointImage;
} }
export interface RichTextUploadVideoNode extends RichTextUploadNode { export interface RichTextUploadVideoNode extends RichTextUploadNode {
relationTo: Collections.Videos; relationTo: Collections.Videos;
value: Video; value: EndpointVideo;
} }
export interface RichTextUploadAudioNode extends RichTextUploadNode { export interface RichTextUploadAudioNode extends RichTextUploadNode {
relationTo: Collections.Audios; relationTo: Collections.Audios;
value: Audio; value: EndpointAudio;
} }
export interface RichTextTextNode extends RichTextNode { export interface RichTextTextNode extends RichTextNode {
@ -1179,10 +1178,10 @@ export const isNodeUploadNode = (node: RichTextNode): node is RichTextUploadNode
export const isUploadNodeImageNode = (node: RichTextUploadNode): node is RichTextUploadImageNode => export const isUploadNodeImageNode = (node: RichTextUploadNode): node is RichTextUploadImageNode =>
node.relationTo === Collections.Images; node.relationTo === Collections.Images;
export const isUploadNodeVideoNode = (node: RichTextUploadNode): node is RichTextUploadVideoNode => export const isUploadNodeVideoNode = (node: RichTextUploadNode): node is RichTextUploadVideoNode =>
node.relationTo === Collections.Videos; node.relationTo === Collections.Videos;
export const isUploadNodeAudioNode = (node: RichTextUploadNode): node is RichTextUploadAudioNode => export const isUploadNodeAudioNode = (node: RichTextUploadNode): node is RichTextUploadAudioNode =>
node.relationTo === Collections.Audios; node.relationTo === Collections.Audios;
export const isNodeListNode = (node: RichTextNode): node is RichTextListNode => export const isNodeListNode = (node: RichTextNode): node is RichTextListNode =>
@ -1338,7 +1337,7 @@ const request = async (url: string, init?: RequestInit): Promise<Response> => {
// SDK and Types // SDK and Types
export type EndpointFolderPreview = { export type EndpointFolder = {
slug: string; slug: string;
icon?: string; icon?: string;
translations: { translations: {
@ -1346,33 +1345,42 @@ export type EndpointFolderPreview = {
name: string; name: string;
description?: RichTextContent; description?: RichTextContent;
}[]; }[];
};
export type EndpointFolder = EndpointFolderPreview & {
sections: sections:
| { type: "single"; subfolders: EndpointFolderPreview[] } | { type: "single"; subfolders: EndpointFolder[] }
| { | {
type: "multiple"; type: "multiple";
sections: { sections: {
translations: { language: string; name: string }[]; translations: { language: string; name: string }[];
subfolders: EndpointFolderPreview[]; subfolders: EndpointFolder[];
}[]; }[];
}; };
files: ( files: (
| { | {
relationTo: "collectibles"; relationTo: Collections.Collectibles;
value: EndpointCollectiblePreview; value: EndpointCollectible;
} }
| { | {
relationTo: "pages"; relationTo: Collections.Pages;
value: EndpointPagePreview; value: EndpointPage;
}
| {
relationTo: Collections.Images;
value: EndpointImage;
}
| {
relationTo: Collections.Audios;
value: EndpointAudio;
}
| {
relationTo: Collections.Videos;
value: EndpointVideo;
} }
)[]; )[];
parentPages: EndpointSource[]; parentPages: EndpointSource[];
}; };
export type EndpointWebsiteConfig = { export type EndpointWebsiteConfig = {
homeFolders: (EndpointFolderPreview & { homeFolders: (EndpointFolder & {
lightThumbnail?: PayloadImage; lightThumbnail?: PayloadImage;
darkThumbnail?: PayloadImage; darkThumbnail?: PayloadImage;
})[]; })[];
@ -1420,23 +1428,18 @@ export type EndpointTagsGroup = {
tags: EndpointTag[]; tags: EndpointTag[];
}; };
export type EndpointPagePreview = { export type EndpointPage = {
slug: string; slug: string;
type: PageType; type: PageType;
thumbnail?: PayloadImage; thumbnail?: PayloadImage;
authors: EndpointRecorder[]; authors: EndpointRecorder[];
tagGroups: EndpointTagsGroup[]; tagGroups: EndpointTagsGroup[];
backgroundImage?: PayloadImage;
translations: { translations: {
language: string; language: string;
pretitle?: string; pretitle?: string;
title: string; title: string;
subtitle?: string; subtitle?: string;
}[];
};
export type EndpointPage = EndpointPagePreview & {
backgroundImage?: PayloadImage;
translations: (EndpointPagePreview["translations"][number] & {
sourceLanguage: string; sourceLanguage: string;
summary?: RichTextContent; summary?: RichTextContent;
content: RichTextContent; content: RichTextContent;
@ -1444,11 +1447,11 @@ export type EndpointPage = EndpointPagePreview & {
translators: EndpointRecorder[]; translators: EndpointRecorder[];
proofreaders: EndpointRecorder[]; proofreaders: EndpointRecorder[];
toc: TableOfContentEntry[]; toc: TableOfContentEntry[];
})[]; }[];
parentPages: EndpointSource[]; parentPages: EndpointSource[];
}; };
export type EndpointCollectiblePreview = { export type EndpointCollectible = {
slug: string; slug: string;
thumbnail?: PayloadImage; thumbnail?: PayloadImage;
translations: { translations: {
@ -1461,9 +1464,6 @@ export type EndpointCollectiblePreview = {
tagGroups: EndpointTagsGroup[]; tagGroups: EndpointTagsGroup[];
releaseDate?: string; releaseDate?: string;
languages: string[]; languages: string[];
};
export type EndpointCollectible = EndpointCollectiblePreview & {
backgroundImage?: PayloadImage; backgroundImage?: PayloadImage;
nature: CollectibleNature; nature: CollectibleNature;
gallery: PayloadImage[]; gallery: PayloadImage[];
@ -1484,15 +1484,23 @@ export type EndpointCollectible = EndpointCollectiblePreview & {
bindingType?: CollectibleBindingTypes; bindingType?: CollectibleBindingTypes;
pageOrder?: CollectiblePageOrders; pageOrder?: CollectiblePageOrders;
}; };
subitems: EndpointCollectiblePreview[]; subitems: EndpointCollectible[];
contents: { contents: {
content: content:
| { | {
relationTo: "pages"; relationTo: Collections.Pages;
value: EndpointPagePreview; value: EndpointPage;
} }
| { | {
relationTo: "generic-contents"; relationTo: Collections.Audios;
value: EndpointAudio;
}
| {
relationTo: Collections.Videos;
value: EndpointVideo;
}
| {
relationTo: Collections.GenericContents;
value: { value: {
translations: { translations: {
language: string; language: string;
@ -1557,21 +1565,72 @@ export type EndpointSource =
| { type: "url"; url: string; label: string } | { type: "url"; url: string; label: string }
| { | {
type: "collectible"; type: "collectible";
collectible: EndpointCollectiblePreview; collectible: EndpointCollectible;
range?: range?:
| { type: "page"; page: number } | { type: "page"; page: number }
| { type: "timestamp"; timestamp: string } | { type: "timestamp"; timestamp: string }
| { type: "custom"; translations: { language: string; note: string }[] }; | { type: "custom"; translations: { language: string; note: string }[] };
} }
| { type: "page"; page: EndpointPagePreview } | { type: "page"; page: EndpointPage }
| { type: "folder"; folder: EndpointFolderPreview }; | { type: "folder"; folder: EndpointFolder };
export type PayloadImage = { export type EndpointMedia = {
id: string;
url: string; url: string;
filename: string;
mimeType: string;
filesize: number;
updatedAt: string;
createdAt: string;
tagGroups: EndpointTagsGroup[];
translations: {
language: string;
title: string;
description?: RichTextContent;
}[];
};
export type EndpointImage = EndpointMedia & {
width: number; width: number;
height: number; height: number;
};
export type EndpointAudio = EndpointMedia & {
thumbnail?: PayloadImage;
duration: number;
};
export type EndpointVideo = EndpointMedia & {
thumbnail?: PayloadImage;
subtitles: {
language: string;
url: string;
}[];
platform?: {
channel: {
url: string;
title: string;
subscribers: number;
};
views?: number;
likes?: number;
dislikes?: number;
url: string;
publishedDate: string;
};
duration: number;
};
export type PayloadMedia = {
url: string;
mimeType: string; mimeType: string;
filename: string; filename: string;
filesize: number;
};
export type PayloadImage = PayloadMedia & {
width: number;
height: number;
}; };
export const payload = { export const payload = {
@ -1595,4 +1654,10 @@ export const payload = {
await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(), await (await request(payloadApiUrl(Collections.ChronologyEvents, `all`))).json(),
getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> => getChronologyEventByID: async (id: string): Promise<EndpointChronologyEvent> =>
await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(), await (await request(payloadApiUrl(Collections.ChronologyEvents, `id/${id}`))).json(),
getImageByID: async (id: string): Promise<EndpointImage> =>
await (await request(payloadApiUrl(Collections.Images, `id/${id}`))).json(),
getAudioByID: async (id: string): Promise<EndpointAudio> =>
await (await request(payloadApiUrl(Collections.Audios, `id/${id}`))).json(),
getVideoByID: async (id: string): Promise<EndpointVideo> =>
await (await request(payloadApiUrl(Collections.Videos, `id/${id}`))).json(),
}; };