Added pretitle and subtitle to media
This commit is contained in:
parent
7700146cf5
commit
b0057192c4
11
TODO.md
11
TODO.md
|
@ -2,12 +2,14 @@
|
|||
|
||||
## Short term
|
||||
|
||||
- [Timeline] inline links to pages not working (timeline 2026/06)
|
||||
- Fix inconsistency with material-icon in Payload (with or without material-icon prefix)
|
||||
- [Media] display filename alongside the localized title.
|
||||
- [Media] Have a title, subtitle, pretitle just like everything else
|
||||
- Background images some times lack gradient at the bottom and fade in before they could load
|
||||
- Number of audio players seems limited (on Chrome and Firefox)
|
||||
- Create a tool to upload scans images and apply them to collectible
|
||||
- Automatically generate different sizes of images
|
||||
- Handle relationship in RichText Content
|
||||
- On most pages (collectibles + pages), there is a gap in the breakpoints where no thumbnail is displayed.
|
||||
- Add duration on audio/video preview cards
|
||||
- Add proper localization for formatFilesize, formatInches, formatMillimeters, formatPounds, and formatGrams
|
||||
|
||||
## Mid term
|
||||
|
||||
|
@ -24,6 +26,7 @@
|
|||
- 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)
|
||||
- [Videos] see why no video on Firefox and no poster on Chrome https://v3.accords-library.com/en/videos/661b672825d380e548dbb8c8
|
||||
- [Videos] Display platform info + channel page
|
||||
|
||||
## Long term
|
||||
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
import Button from "components/Button.astro";
|
||||
import {
|
||||
type EndpointCredit,
|
||||
type EndpointTagsGroup,
|
||||
type PayloadImage,
|
||||
type RichTextContent,
|
||||
} from "src/shared/payload/payload-sdk";
|
||||
|
@ -10,14 +9,18 @@ import RichText from "./RichText/RichText.astro";
|
|||
import TagGroups from "./TagGroups.astro";
|
||||
import Credits from "./Credits.astro";
|
||||
import DownloadButton from "./DownloadButton.astro";
|
||||
import AppLayoutTitle from "./AppLayout/components/AppLayoutTitle.astro";
|
||||
import type { ComponentProps } from "astro/types";
|
||||
|
||||
interface Props {
|
||||
previousImageHref?: string | undefined;
|
||||
nextImageHref?: string | undefined;
|
||||
image: PayloadImage;
|
||||
pretitle?: string | undefined;
|
||||
title: string;
|
||||
subtitle?: string | undefined;
|
||||
description?: RichTextContent | undefined;
|
||||
tagGroups?: EndpointTagsGroup[] | undefined;
|
||||
tagGroups?: ComponentProps<typeof TagGroups>["tagGroups"] | undefined;
|
||||
credits?: EndpointCredit[] | undefined;
|
||||
filename?: string | undefined;
|
||||
}
|
||||
|
@ -26,12 +29,16 @@ const {
|
|||
nextImageHref,
|
||||
previousImageHref,
|
||||
image: { url, width, height },
|
||||
tagGroups,
|
||||
tagGroups = [],
|
||||
credits,
|
||||
description,
|
||||
pretitle,
|
||||
title,
|
||||
subtitle,
|
||||
filename,
|
||||
} = Astro.props;
|
||||
|
||||
const smallTitle = !subtitle && !pretitle;
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
@ -58,12 +65,17 @@ const {
|
|||
complex:
|
||||
(tagGroups && tagGroups.length > 0) || (credits && credits.length > 0) || description,
|
||||
}}>
|
||||
<h1>{title}</h1>
|
||||
{
|
||||
smallTitle ? (
|
||||
<h1>{title}</h1>
|
||||
) : (
|
||||
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||
)
|
||||
}
|
||||
|
||||
{description && <RichText content={description} />}
|
||||
<div>
|
||||
{tagGroups && tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
||||
{credits && credits.length > 0 && <Credits credits={credits} />}
|
||||
</div>
|
||||
{tagGroups && tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
|
||||
{credits && credits.length > 0 && <Credits credits={credits} />}
|
||||
{filename && <DownloadButton href={url} filename={filename} />}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,9 +5,10 @@ interface Props {
|
|||
icon: string;
|
||||
title: string;
|
||||
values: string[];
|
||||
withBorder?: boolean | undefined;
|
||||
}
|
||||
|
||||
const { icon, title, values } = Astro.props;
|
||||
const { icon, title, values, withBorder = true } = Astro.props;
|
||||
|
||||
if (values.length === 0) return;
|
||||
---
|
||||
|
@ -19,7 +20,7 @@ if (values.length === 0) return;
|
|||
<Icon name={icon} width={24} height={24} />
|
||||
<p>{title}</p>
|
||||
</div>
|
||||
<div id="values">
|
||||
<div id="values" class:list={{ "with-border": withBorder }}>
|
||||
{values.map((value) => <div>{value}</div>)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -54,13 +55,15 @@ if (values.length === 0) return;
|
|||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
|
||||
& > div {
|
||||
border: 1px solid var(--color-base-1000);
|
||||
border-radius: 9999px;
|
||||
padding-top: 0.15em;
|
||||
padding-bottom: 0.25em;
|
||||
padding-inline: 0.6em;
|
||||
backdrop-filter: blur(10px);
|
||||
&.with-border {
|
||||
& > div {
|
||||
border: 1px solid var(--color-base-1000);
|
||||
border-radius: 9999px;
|
||||
padding-bottom: 0.25em;
|
||||
padding-top: 0.15em;
|
||||
padding-inline: 0.6em;
|
||||
backdrop-filter: blur(10upx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,32 +7,39 @@ interface Props {
|
|||
audio: EndpointAudio;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
||||
const { getLocalizedMatch, getLocalizedUrl, t, formatDuration } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const {
|
||||
audio: { id, translations, tagGroups, filename, thumbnail },
|
||||
audio: { id, translations, tagGroups, filename, thumbnail, duration },
|
||||
} = Astro.props;
|
||||
|
||||
const { title } = translations.length > 0 ? getLocalizedMatch(translations) : { title: filename };
|
||||
const { pretitle, title, subtitle } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
: { pretitle: undefined, title: filename, subtitle: undefined };
|
||||
|
||||
// TODO: Add this later
|
||||
// const attributes = [
|
||||
// {
|
||||
// title: "Duration",
|
||||
// icon: "material-symbols:hourglass-empty",
|
||||
// values: [duration.toString()],
|
||||
// },
|
||||
// ];
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
{
|
||||
title: t("global.media.attributes.duration"),
|
||||
icon: "material-symbols:hourglass-empty",
|
||||
values: [formatDuration(duration)],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<GenericPreview
|
||||
pretitle={pretitle}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
thumbnail={thumbnail}
|
||||
href={getLocalizedUrl(`/audios/${id}`)}
|
||||
tagGroups={tagGroups}
|
||||
tagGroups={tagsAndAttributes}
|
||||
icon="material-symbols:music-note"
|
||||
iconHoverLabel={t("global.previewTypes.audio")}
|
||||
smallTitle
|
||||
smallTitle={title === filename}
|
||||
/>
|
||||
|
|
|
@ -14,17 +14,22 @@ const {
|
|||
image: { id, translations, tagGroups, filename },
|
||||
} = Astro.props;
|
||||
|
||||
const { title } = translations.length > 0 ? getLocalizedMatch(translations) : { title: filename };
|
||||
const { pretitle, title, subtitle } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
: { pretitle: undefined, title: filename, subtitle: undefined };
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<GenericPreview
|
||||
pretitle={pretitle}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
thumbnail={thumbnail}
|
||||
href={getLocalizedUrl(`/images/${id}`)}
|
||||
tagGroups={tagGroups}
|
||||
icon="material-symbols:imagesmode"
|
||||
iconHoverLabel={t("global.previewTypes.image")}
|
||||
smallTitle
|
||||
smallTitle={title === filename}
|
||||
/>
|
||||
|
|
|
@ -7,23 +7,39 @@ interface Props {
|
|||
video: EndpointVideo;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
||||
const { getLocalizedMatch, getLocalizedUrl, t, formatDuration } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const {
|
||||
video: { id, translations, tagGroups, filename, thumbnail },
|
||||
video: { id, translations, tagGroups, filename, thumbnail, duration },
|
||||
} = Astro.props;
|
||||
|
||||
const { title } = translations.length > 0 ? getLocalizedMatch(translations) : { title: filename };
|
||||
const { pretitle, title, subtitle } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
: { pretitle: undefined, title: filename, subtitle: undefined };
|
||||
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
{
|
||||
title: t("global.media.attributes.duration"),
|
||||
icon: "material-symbols:hourglass-empty",
|
||||
values: [formatDuration(duration)],
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<GenericPreview
|
||||
pretitle={pretitle}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
thumbnail={thumbnail}
|
||||
href={getLocalizedUrl(`/videos/${id}`)}
|
||||
tagGroups={tagGroups}
|
||||
tagGroups={tagsAndAttributes}
|
||||
icon="material-symbols:smart-display"
|
||||
iconHoverLabel={t("global.previewTypes.video")}
|
||||
smallTitle
|
||||
smallTitle={title === filename}
|
||||
/>
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
---
|
||||
import Metadata from "components/Metadata.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import type { EndpointTagsGroup } from "src/shared/payload/payload-sdk";
|
||||
|
||||
interface Props {
|
||||
tagGroup: EndpointTagsGroup;
|
||||
}
|
||||
|
||||
const {
|
||||
tagGroup: { icon, translations, tags },
|
||||
} = Astro.props;
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<Metadata
|
||||
icon={icon}
|
||||
title={getLocalizedMatch(translations).name}
|
||||
values={tags.map(({ translations }) => getLocalizedMatch(translations).name)}
|
||||
/>
|
|
@ -1,18 +1,39 @@
|
|||
---
|
||||
import type { EndpointTagsGroup } from "src/shared/payload/payload-sdk";
|
||||
import TagGroup from "./TagGroup.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import Metadata from "./Metadata.astro";
|
||||
|
||||
interface Props {
|
||||
tagGroups: EndpointTagsGroup[];
|
||||
tagGroups: (
|
||||
| EndpointTagsGroup
|
||||
| { title: string; icon: string; values: string[]; withBorder?: boolean }
|
||||
)[];
|
||||
}
|
||||
|
||||
const { tagGroups } = Astro.props;
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
|
||||
const groups = tagGroups.map((group) => {
|
||||
if ("title" in group) {
|
||||
return group;
|
||||
} else {
|
||||
return {
|
||||
title: getLocalizedMatch(group.translations).name,
|
||||
icon: group.icon,
|
||||
values: group.tags.map(({ translations }) => getLocalizedMatch(translations).name),
|
||||
};
|
||||
}
|
||||
});
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<div>
|
||||
{tagGroups.map((tag) => <TagGroup tagGroup={tag} />)}
|
||||
{
|
||||
groups.map(({ icon, title, values, withBorder }) => (
|
||||
<Metadata icon={icon} title={title} values={values} withBorder={withBorder} />
|
||||
))
|
||||
}
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
|
|
|
@ -134,6 +134,25 @@ export const getI18n = async (locale: string) => {
|
|||
options: Intl.DateTimeFormatOptions | undefined = { dateStyle: "medium" }
|
||||
): string => date.toLocaleDateString(locale, options);
|
||||
|
||||
const formatDuration = (durationInSec: number) => {
|
||||
const hours = Math.floor(durationInSec / 3600);
|
||||
durationInSec -= hours * 3600;
|
||||
const minutes = Math.floor(durationInSec / 60);
|
||||
durationInSec -= minutes * 60;
|
||||
const seconds = Math.floor(durationInSec);
|
||||
return `${hours.toString().padStart(2, "0")}:${minutes.toString().padStart(2, "0")}:${seconds.toString().padStart(2, "0")}`;
|
||||
};
|
||||
|
||||
const formatFilesize = (sizeInBytes: number): string => {
|
||||
if (sizeInBytes < 1_000) return `${formatNumber(sizeInBytes, { maximumFractionDigits: 2 })} B`;
|
||||
sizeInBytes = sizeInBytes / 1000;
|
||||
if (sizeInBytes < 1_000) return `${formatNumber(sizeInBytes, { maximumFractionDigits: 2 })} KB`;
|
||||
sizeInBytes = sizeInBytes / 1000;
|
||||
if (sizeInBytes < 1_000) return `${formatNumber(sizeInBytes, { maximumFractionDigits: 2 })} MB`;
|
||||
sizeInBytes = sizeInBytes / 1000;
|
||||
return `${formatNumber(sizeInBytes, { maximumFractionDigits: 2 })} GB`;
|
||||
};
|
||||
|
||||
const formatInches = (sizeInMm: number): string => {
|
||||
return (
|
||||
(sizeInMm * 0.039370078740157).toLocaleString(locale, { maximumFractionDigits: 2 }) + " in"
|
||||
|
@ -301,6 +320,7 @@ export const getI18n = async (locale: string) => {
|
|||
getLocalizedUrl,
|
||||
formatPrice,
|
||||
formatDate,
|
||||
formatDuration,
|
||||
formatInches,
|
||||
formatPounds,
|
||||
formatGrams,
|
||||
|
@ -309,5 +329,6 @@ export const getI18n = async (locale: string) => {
|
|||
formatTimelineDate,
|
||||
formatEndpointSource,
|
||||
formatScanIndexShort,
|
||||
formatFilesize,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -139,4 +139,9 @@ export type WordingKey =
|
|||
| "global.sources.typeLabel.scans"
|
||||
| "collectibles.scans.dustjacket.description"
|
||||
| "collectibles.scans.obi.description"
|
||||
| "global.sources.typeLabel.gallery";
|
||||
| "global.sources.typeLabel.gallery"
|
||||
| "global.media.attributes.filename"
|
||||
| "global.media.attributes.duration"
|
||||
| "global.media.attributes.filesize"
|
||||
| "global.media.attributes.createdAt"
|
||||
| "global.media.attributes.updatedAt";
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||
import AudioPlayer from "components/AudioPlayer.astro";
|
||||
import Credits from "components/Credits.astro";
|
||||
import DownloadButton from "components/DownloadButton.astro";
|
||||
|
@ -7,6 +8,7 @@ import RichText from "components/RichText/RichText.astro";
|
|||
import TagGroups from "components/TagGroups.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { payload } from "src/shared/payload/payload-sdk";
|
||||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
@ -15,23 +17,81 @@ if (audio instanceof Response) {
|
|||
return audio;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const { translations, tagGroups, filename, url, credits } = audio;
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
const {
|
||||
translations,
|
||||
tagGroups,
|
||||
filename,
|
||||
url,
|
||||
credits,
|
||||
filesize,
|
||||
createdAt,
|
||||
updatedAt,
|
||||
thumbnail,
|
||||
} = audio;
|
||||
|
||||
const { title, description } = getLocalizedMatch(translations);
|
||||
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations);
|
||||
|
||||
const smallTitle = !subtitle && !pretitle;
|
||||
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
...(filename && title !== filename
|
||||
? [
|
||||
{
|
||||
title: t("global.media.attributes.filename"),
|
||||
icon: "material-symbols:audio-file-outline",
|
||||
values: [filename],
|
||||
withBorder: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("global.media.attributes.filesize"),
|
||||
icon: "material-symbols:hard-drive-outline",
|
||||
values: [formatFilesize(filesize)],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.createdAt"),
|
||||
icon: "material-symbols:calendar-add-on-outline",
|
||||
values: [formatDate(new Date(createdAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.updatedAt"),
|
||||
icon: "material-symbols:edit-calendar",
|
||||
values: [formatDate(new Date(updatedAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<AppEmptyLayout>
|
||||
<AppEmptyLayout
|
||||
openGraph={{
|
||||
thumbnail,
|
||||
audio,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
}}>
|
||||
<div id="container">
|
||||
<AudioPlayer audio={audio} />
|
||||
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
{
|
||||
smallTitle ? (
|
||||
<h1>{title}</h1>
|
||||
) : (
|
||||
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||
)
|
||||
}
|
||||
{description && <RichText content={description} />}
|
||||
<div>
|
||||
{tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
||||
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
|
||||
{credits.length > 0 && <Credits credits={credits} />}
|
||||
</div>
|
||||
<DownloadButton href={url} filename={filename} />
|
||||
|
|
|
@ -7,7 +7,9 @@ import { fetchOr404 } from "src/utils/responses";
|
|||
|
||||
const slug = Astro.params.slug!;
|
||||
const index = Astro.params.index!;
|
||||
const { getLocalizedUrl, getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const { getLocalizedUrl, getLocalizedMatch, t, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
|
||||
const galleryImage = await fetchOr404(() => payload.getCollectibleGalleryImage(slug, index));
|
||||
if (galleryImage instanceof Response) {
|
||||
|
@ -15,11 +17,38 @@ if (galleryImage instanceof Response) {
|
|||
}
|
||||
|
||||
const { parentPages, previousIndex, nextIndex, image } = galleryImage;
|
||||
const { filename, translations, createdAt, updatedAt, credits, tagGroups } = image;
|
||||
|
||||
const { title, description } =
|
||||
image.translations.length > 0
|
||||
? getLocalizedMatch(image.translations)
|
||||
: { title: image.filename, description: undefined };
|
||||
const { pretitle, title, subtitle, description } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
: { pretitle: undefined, title: filename, subtitle: undefined, description: undefined };
|
||||
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
...(filename && title !== filename
|
||||
? [
|
||||
{
|
||||
title: t("global.media.attributes.filename"),
|
||||
icon: "material-symbols:unknown-document-outline",
|
||||
values: [filename],
|
||||
withBorder: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("global.media.attributes.createdAt"),
|
||||
icon: "material-symbols:calendar-add-on-outline",
|
||||
values: [formatDate(new Date(createdAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.updatedAt"),
|
||||
icon: "material-symbols:edit-calendar",
|
||||
values: [formatDate(new Date(updatedAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
@ -27,7 +56,9 @@ const { title, description } =
|
|||
<AppEmptyLayout parentPages={parentPages}>
|
||||
<Lightbox
|
||||
image={image}
|
||||
pretitle={pretitle}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
previousImageHref={previousIndex
|
||||
? getLocalizedUrl(`/collectibles/${slug}/gallery/${previousIndex}`)
|
||||
: undefined}
|
||||
|
@ -35,8 +66,8 @@ const { title, description } =
|
|||
? getLocalizedUrl(`/collectibles/${slug}/gallery/${nextIndex}`)
|
||||
: undefined}
|
||||
description={description}
|
||||
filename={image.filename}
|
||||
tagGroups={image.tagGroups}
|
||||
credits={image.credits}
|
||||
filename={filename}
|
||||
tagGroups={tagsAndAttributes}
|
||||
credits={credits}
|
||||
/>
|
||||
</AppEmptyLayout>
|
||||
|
|
|
@ -3,6 +3,7 @@ import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
|||
import Lightbox from "components/Lightbox.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { payload } from "src/shared/payload/payload-sdk";
|
||||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
@ -11,24 +12,57 @@ if (image instanceof Response) {
|
|||
return image;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const { filename, translations, tagGroups, credits } = image;
|
||||
const { getLocalizedMatch, formatDate, t } = await getI18n(Astro.locals.currentLocale);
|
||||
const { filename, translations, tagGroups, credits, createdAt, updatedAt } = image;
|
||||
|
||||
const { title, description } =
|
||||
const { pretitle, title, subtitle, description } =
|
||||
translations.length > 0
|
||||
? getLocalizedMatch(translations)
|
||||
: { title: filename, description: undefined };
|
||||
: { pretitle: undefined, title: filename, subtitle: undefined, description: undefined };
|
||||
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
...(filename && title !== filename
|
||||
? [
|
||||
{
|
||||
title: t("global.media.attributes.filename"),
|
||||
icon: "material-symbols:unknown-document-outline",
|
||||
values: [filename],
|
||||
withBorder: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("global.media.attributes.createdAt"),
|
||||
icon: "material-symbols:calendar-add-on-outline",
|
||||
values: [formatDate(new Date(createdAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.updatedAt"),
|
||||
icon: "material-symbols:edit-calendar",
|
||||
values: [formatDate(new Date(updatedAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<AppEmptyLayout>
|
||||
<AppEmptyLayout
|
||||
openGraph={{
|
||||
thumbnail: image,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
}}>
|
||||
<Lightbox
|
||||
image={image}
|
||||
pretitle={pretitle}
|
||||
title={title}
|
||||
subtitle={subtitle}
|
||||
description={description}
|
||||
filename={filename}
|
||||
tagGroups={tagGroups}
|
||||
tagGroups={tagsAndAttributes}
|
||||
credits={credits}
|
||||
/>
|
||||
</AppEmptyLayout>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
---
|
||||
import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||
import Credits from "components/Credits.astro";
|
||||
import DownloadButton from "components/DownloadButton.astro";
|
||||
import RichText from "components/RichText/RichText.astro";
|
||||
|
@ -7,6 +8,7 @@ import TagGroups from "components/TagGroups.astro";
|
|||
import VideoPlayer from "components/VideoPlayer.astro";
|
||||
import { getI18n } from "src/i18n/i18n";
|
||||
import { payload } from "src/shared/payload/payload-sdk";
|
||||
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||
import { fetchOr404 } from "src/utils/responses";
|
||||
|
||||
const { id } = Astro.params;
|
||||
|
@ -15,23 +17,80 @@ if (video instanceof Response) {
|
|||
return video;
|
||||
}
|
||||
|
||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
||||
const { translations, tagGroups, filename, url, credits } = video;
|
||||
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||
Astro.locals.currentLocale
|
||||
);
|
||||
const {
|
||||
translations,
|
||||
tagGroups,
|
||||
filename,
|
||||
url,
|
||||
credits,
|
||||
filesize,
|
||||
updatedAt,
|
||||
createdAt,
|
||||
thumbnail,
|
||||
} = video;
|
||||
|
||||
const { title, description } = getLocalizedMatch(translations);
|
||||
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations);
|
||||
const smallTitle = !subtitle && !pretitle;
|
||||
|
||||
const tagsAndAttributes = [
|
||||
...tagGroups,
|
||||
...(filename && title !== filename
|
||||
? [
|
||||
{
|
||||
title: t("global.media.attributes.filename"),
|
||||
icon: "material-symbols:video-file-outline",
|
||||
values: [filename],
|
||||
withBorder: false,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
title: t("global.media.attributes.filesize"),
|
||||
icon: "material-symbols:hard-drive-outline",
|
||||
values: [formatFilesize(filesize)],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.createdAt"),
|
||||
icon: "material-symbols:calendar-add-on-outline",
|
||||
values: [formatDate(new Date(createdAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
{
|
||||
title: t("global.media.attributes.updatedAt"),
|
||||
icon: "material-symbols:edit-calendar",
|
||||
values: [formatDate(new Date(updatedAt))],
|
||||
withBorder: false,
|
||||
},
|
||||
];
|
||||
---
|
||||
|
||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||
|
||||
<AppEmptyLayout>
|
||||
<AppEmptyLayout
|
||||
openGraph={{
|
||||
thumbnail,
|
||||
video,
|
||||
description: description ? formatRichTextToString(description) : undefined,
|
||||
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||
}}>
|
||||
<div id="container">
|
||||
<VideoPlayer video={video} />
|
||||
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
{
|
||||
smallTitle ? (
|
||||
<h1>{title}</h1>
|
||||
) : (
|
||||
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||
)
|
||||
}
|
||||
{description && <RichText content={description} />}
|
||||
<div>
|
||||
{tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
||||
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
|
||||
{credits.length > 0 && <Credits credits={credits} />}
|
||||
</div>
|
||||
<DownloadButton href={url} filename={filename} />
|
||||
|
|
|
@ -111,7 +111,9 @@ export interface Image {
|
|||
translations?:
|
||||
| {
|
||||
language: string | Language;
|
||||
pretitle?: string | null;
|
||||
title: string;
|
||||
subtitle?: string | null;
|
||||
description?: {
|
||||
root: {
|
||||
type: string;
|
||||
|
@ -559,7 +561,9 @@ export interface Audio {
|
|||
thumbnail?: string | MediaThumbnail | null;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
pretitle?: string | null;
|
||||
title: string;
|
||||
subtitle?: string | null;
|
||||
description?: {
|
||||
root: {
|
||||
type: string;
|
||||
|
@ -631,7 +635,9 @@ export interface Video {
|
|||
thumbnail?: string | MediaThumbnail | null;
|
||||
translations: {
|
||||
language: string | Language;
|
||||
pretitle?: string | null;
|
||||
title: string;
|
||||
subtitle?: string | null;
|
||||
description?: {
|
||||
root: {
|
||||
type: string;
|
||||
|
@ -1711,7 +1717,9 @@ export type EndpointMedia = {
|
|||
tagGroups: EndpointTagsGroup[];
|
||||
translations: {
|
||||
language: string;
|
||||
pretitle?: string;
|
||||
title: string;
|
||||
subtitle?: string;
|
||||
description?: RichTextContent;
|
||||
}[];
|
||||
credits: EndpointCredit[];
|
||||
|
|
Loading…
Reference in New Issue