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
|
## 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
|
- 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)
|
- 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
|
## 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)
|
- 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)
|
||||||
- [Videos] see why no video on Firefox and no poster on Chrome https://v3.accords-library.com/en/videos/661b672825d380e548dbb8c8
|
- [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
|
## Long term
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
import Button from "components/Button.astro";
|
import Button from "components/Button.astro";
|
||||||
import {
|
import {
|
||||||
type EndpointCredit,
|
type EndpointCredit,
|
||||||
type EndpointTagsGroup,
|
|
||||||
type PayloadImage,
|
type PayloadImage,
|
||||||
type RichTextContent,
|
type RichTextContent,
|
||||||
} from "src/shared/payload/payload-sdk";
|
} from "src/shared/payload/payload-sdk";
|
||||||
|
@ -10,14 +9,18 @@ import RichText from "./RichText/RichText.astro";
|
||||||
import TagGroups from "./TagGroups.astro";
|
import TagGroups from "./TagGroups.astro";
|
||||||
import Credits from "./Credits.astro";
|
import Credits from "./Credits.astro";
|
||||||
import DownloadButton from "./DownloadButton.astro";
|
import DownloadButton from "./DownloadButton.astro";
|
||||||
|
import AppLayoutTitle from "./AppLayout/components/AppLayoutTitle.astro";
|
||||||
|
import type { ComponentProps } from "astro/types";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
previousImageHref?: string | undefined;
|
previousImageHref?: string | undefined;
|
||||||
nextImageHref?: string | undefined;
|
nextImageHref?: string | undefined;
|
||||||
image: PayloadImage;
|
image: PayloadImage;
|
||||||
|
pretitle?: string | undefined;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string | undefined;
|
||||||
description?: RichTextContent | undefined;
|
description?: RichTextContent | undefined;
|
||||||
tagGroups?: EndpointTagsGroup[] | undefined;
|
tagGroups?: ComponentProps<typeof TagGroups>["tagGroups"] | undefined;
|
||||||
credits?: EndpointCredit[] | undefined;
|
credits?: EndpointCredit[] | undefined;
|
||||||
filename?: string | undefined;
|
filename?: string | undefined;
|
||||||
}
|
}
|
||||||
|
@ -26,12 +29,16 @@ const {
|
||||||
nextImageHref,
|
nextImageHref,
|
||||||
previousImageHref,
|
previousImageHref,
|
||||||
image: { url, width, height },
|
image: { url, width, height },
|
||||||
tagGroups,
|
tagGroups = [],
|
||||||
credits,
|
credits,
|
||||||
description,
|
description,
|
||||||
|
pretitle,
|
||||||
title,
|
title,
|
||||||
|
subtitle,
|
||||||
filename,
|
filename,
|
||||||
} = Astro.props;
|
} = Astro.props;
|
||||||
|
|
||||||
|
const smallTitle = !subtitle && !pretitle;
|
||||||
---
|
---
|
||||||
|
|
||||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
@ -58,12 +65,17 @@ const {
|
||||||
complex:
|
complex:
|
||||||
(tagGroups && tagGroups.length > 0) || (credits && credits.length > 0) || description,
|
(tagGroups && tagGroups.length > 0) || (credits && credits.length > 0) || description,
|
||||||
}}>
|
}}>
|
||||||
|
{
|
||||||
|
smallTitle ? (
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
) : (
|
||||||
|
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
{description && <RichText content={description} />}
|
{description && <RichText content={description} />}
|
||||||
<div>
|
{tagGroups && tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
|
||||||
{tagGroups && tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
|
||||||
{credits && credits.length > 0 && <Credits credits={credits} />}
|
{credits && credits.length > 0 && <Credits credits={credits} />}
|
||||||
</div>
|
|
||||||
{filename && <DownloadButton href={url} filename={filename} />}
|
{filename && <DownloadButton href={url} filename={filename} />}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -5,9 +5,10 @@ interface Props {
|
||||||
icon: string;
|
icon: string;
|
||||||
title: string;
|
title: string;
|
||||||
values: 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;
|
if (values.length === 0) return;
|
||||||
---
|
---
|
||||||
|
@ -19,7 +20,7 @@ if (values.length === 0) return;
|
||||||
<Icon name={icon} width={24} height={24} />
|
<Icon name={icon} width={24} height={24} />
|
||||||
<p>{title}</p>
|
<p>{title}</p>
|
||||||
</div>
|
</div>
|
||||||
<div id="values">
|
<div id="values" class:list={{ "with-border": withBorder }}>
|
||||||
{values.map((value) => <div>{value}</div>)}
|
{values.map((value) => <div>{value}</div>)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -54,13 +55,15 @@ if (values.length === 0) return;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
|
|
||||||
|
&.with-border {
|
||||||
& > div {
|
& > div {
|
||||||
border: 1px solid var(--color-base-1000);
|
border: 1px solid var(--color-base-1000);
|
||||||
border-radius: 9999px;
|
border-radius: 9999px;
|
||||||
padding-top: 0.15em;
|
|
||||||
padding-bottom: 0.25em;
|
padding-bottom: 0.25em;
|
||||||
|
padding-top: 0.15em;
|
||||||
padding-inline: 0.6em;
|
padding-inline: 0.6em;
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(10upx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,32 +7,39 @@ interface Props {
|
||||||
audio: EndpointAudio;
|
audio: EndpointAudio;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
const { getLocalizedMatch, getLocalizedUrl, t, formatDuration } = await getI18n(
|
||||||
|
Astro.locals.currentLocale
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
audio: { id, translations, tagGroups, filename, thumbnail },
|
audio: { id, translations, tagGroups, filename, thumbnail, duration },
|
||||||
} = Astro.props;
|
} = 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 tagsAndAttributes = [
|
||||||
// const attributes = [
|
...tagGroups,
|
||||||
// {
|
{
|
||||||
// title: "Duration",
|
title: t("global.media.attributes.duration"),
|
||||||
// icon: "material-symbols:hourglass-empty",
|
icon: "material-symbols:hourglass-empty",
|
||||||
// values: [duration.toString()],
|
values: [formatDuration(duration)],
|
||||||
// },
|
},
|
||||||
// ];
|
];
|
||||||
---
|
---
|
||||||
|
|
||||||
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<GenericPreview
|
<GenericPreview
|
||||||
|
pretitle={pretitle}
|
||||||
title={title}
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
thumbnail={thumbnail}
|
thumbnail={thumbnail}
|
||||||
href={getLocalizedUrl(`/audios/${id}`)}
|
href={getLocalizedUrl(`/audios/${id}`)}
|
||||||
tagGroups={tagGroups}
|
tagGroups={tagsAndAttributes}
|
||||||
icon="material-symbols:music-note"
|
icon="material-symbols:music-note"
|
||||||
iconHoverLabel={t("global.previewTypes.audio")}
|
iconHoverLabel={t("global.previewTypes.audio")}
|
||||||
smallTitle
|
smallTitle={title === filename}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -14,17 +14,22 @@ const {
|
||||||
image: { id, translations, tagGroups, filename },
|
image: { id, translations, tagGroups, filename },
|
||||||
} = Astro.props;
|
} = 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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<GenericPreview
|
<GenericPreview
|
||||||
|
pretitle={pretitle}
|
||||||
title={title}
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
thumbnail={thumbnail}
|
thumbnail={thumbnail}
|
||||||
href={getLocalizedUrl(`/images/${id}`)}
|
href={getLocalizedUrl(`/images/${id}`)}
|
||||||
tagGroups={tagGroups}
|
tagGroups={tagGroups}
|
||||||
icon="material-symbols:imagesmode"
|
icon="material-symbols:imagesmode"
|
||||||
iconHoverLabel={t("global.previewTypes.image")}
|
iconHoverLabel={t("global.previewTypes.image")}
|
||||||
smallTitle
|
smallTitle={title === filename}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -7,23 +7,39 @@ interface Props {
|
||||||
video: EndpointVideo;
|
video: EndpointVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
|
const { getLocalizedMatch, getLocalizedUrl, t, formatDuration } = await getI18n(
|
||||||
|
Astro.locals.currentLocale
|
||||||
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
video: { id, translations, tagGroups, filename, thumbnail },
|
video: { id, translations, tagGroups, filename, thumbnail, duration },
|
||||||
} = Astro.props;
|
} = 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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<GenericPreview
|
<GenericPreview
|
||||||
|
pretitle={pretitle}
|
||||||
title={title}
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
thumbnail={thumbnail}
|
thumbnail={thumbnail}
|
||||||
href={getLocalizedUrl(`/videos/${id}`)}
|
href={getLocalizedUrl(`/videos/${id}`)}
|
||||||
tagGroups={tagGroups}
|
tagGroups={tagsAndAttributes}
|
||||||
icon="material-symbols:smart-display"
|
icon="material-symbols:smart-display"
|
||||||
iconHoverLabel={t("global.previewTypes.video")}
|
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 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 {
|
interface Props {
|
||||||
tagGroups: EndpointTagsGroup[];
|
tagGroups: (
|
||||||
|
| EndpointTagsGroup
|
||||||
|
| { title: string; icon: string; values: string[]; withBorder?: boolean }
|
||||||
|
)[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const { tagGroups } = Astro.props;
|
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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
{tagGroups.map((tag) => <TagGroup tagGroup={tag} />)}
|
{
|
||||||
|
groups.map(({ icon, title, values, withBorder }) => (
|
||||||
|
<Metadata icon={icon} title={title} values={values} withBorder={withBorder} />
|
||||||
|
))
|
||||||
|
}
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -134,6 +134,25 @@ export const getI18n = async (locale: string) => {
|
||||||
options: Intl.DateTimeFormatOptions | undefined = { dateStyle: "medium" }
|
options: Intl.DateTimeFormatOptions | undefined = { dateStyle: "medium" }
|
||||||
): string => date.toLocaleDateString(locale, options);
|
): 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 => {
|
const formatInches = (sizeInMm: number): string => {
|
||||||
return (
|
return (
|
||||||
(sizeInMm * 0.039370078740157).toLocaleString(locale, { maximumFractionDigits: 2 }) + " in"
|
(sizeInMm * 0.039370078740157).toLocaleString(locale, { maximumFractionDigits: 2 }) + " in"
|
||||||
|
@ -301,6 +320,7 @@ export const getI18n = async (locale: string) => {
|
||||||
getLocalizedUrl,
|
getLocalizedUrl,
|
||||||
formatPrice,
|
formatPrice,
|
||||||
formatDate,
|
formatDate,
|
||||||
|
formatDuration,
|
||||||
formatInches,
|
formatInches,
|
||||||
formatPounds,
|
formatPounds,
|
||||||
formatGrams,
|
formatGrams,
|
||||||
|
@ -309,5 +329,6 @@ export const getI18n = async (locale: string) => {
|
||||||
formatTimelineDate,
|
formatTimelineDate,
|
||||||
formatEndpointSource,
|
formatEndpointSource,
|
||||||
formatScanIndexShort,
|
formatScanIndexShort,
|
||||||
|
formatFilesize,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -139,4 +139,9 @@ export type WordingKey =
|
||||||
| "global.sources.typeLabel.scans"
|
| "global.sources.typeLabel.scans"
|
||||||
| "collectibles.scans.dustjacket.description"
|
| "collectibles.scans.dustjacket.description"
|
||||||
| "collectibles.scans.obi.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 AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||||
|
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||||
import AudioPlayer from "components/AudioPlayer.astro";
|
import AudioPlayer from "components/AudioPlayer.astro";
|
||||||
import Credits from "components/Credits.astro";
|
import Credits from "components/Credits.astro";
|
||||||
import DownloadButton from "components/DownloadButton.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 TagGroups from "components/TagGroups.astro";
|
||||||
import { getI18n } from "src/i18n/i18n";
|
import { getI18n } from "src/i18n/i18n";
|
||||||
import { payload } from "src/shared/payload/payload-sdk";
|
import { payload } from "src/shared/payload/payload-sdk";
|
||||||
|
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||||
import { fetchOr404 } from "src/utils/responses";
|
import { fetchOr404 } from "src/utils/responses";
|
||||||
|
|
||||||
const { id } = Astro.params;
|
const { id } = Astro.params;
|
||||||
|
@ -15,23 +17,81 @@ if (audio instanceof Response) {
|
||||||
return audio;
|
return audio;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||||
const { translations, tagGroups, filename, url, credits } = audio;
|
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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<AppEmptyLayout>
|
<AppEmptyLayout
|
||||||
|
openGraph={{
|
||||||
|
thumbnail,
|
||||||
|
audio,
|
||||||
|
description: description ? formatRichTextToString(description) : undefined,
|
||||||
|
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||||
|
}}>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<AudioPlayer audio={audio} />
|
<AudioPlayer audio={audio} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{
|
||||||
|
smallTitle ? (
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
) : (
|
||||||
|
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||||
|
)
|
||||||
|
}
|
||||||
{description && <RichText content={description} />}
|
{description && <RichText content={description} />}
|
||||||
<div>
|
<div>
|
||||||
{tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
|
||||||
{credits.length > 0 && <Credits credits={credits} />}
|
{credits.length > 0 && <Credits credits={credits} />}
|
||||||
</div>
|
</div>
|
||||||
<DownloadButton href={url} filename={filename} />
|
<DownloadButton href={url} filename={filename} />
|
||||||
|
|
|
@ -7,7 +7,9 @@ import { fetchOr404 } from "src/utils/responses";
|
||||||
|
|
||||||
const slug = Astro.params.slug!;
|
const slug = Astro.params.slug!;
|
||||||
const index = Astro.params.index!;
|
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));
|
const galleryImage = await fetchOr404(() => payload.getCollectibleGalleryImage(slug, index));
|
||||||
if (galleryImage instanceof Response) {
|
if (galleryImage instanceof Response) {
|
||||||
|
@ -15,11 +17,38 @@ if (galleryImage instanceof Response) {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { parentPages, previousIndex, nextIndex, image } = galleryImage;
|
const { parentPages, previousIndex, nextIndex, image } = galleryImage;
|
||||||
|
const { filename, translations, createdAt, updatedAt, credits, tagGroups } = image;
|
||||||
|
|
||||||
const { title, description } =
|
const { pretitle, title, subtitle, description } =
|
||||||
image.translations.length > 0
|
translations.length > 0
|
||||||
? getLocalizedMatch(image.translations)
|
? getLocalizedMatch(translations)
|
||||||
: { title: image.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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
@ -27,7 +56,9 @@ const { title, description } =
|
||||||
<AppEmptyLayout parentPages={parentPages}>
|
<AppEmptyLayout parentPages={parentPages}>
|
||||||
<Lightbox
|
<Lightbox
|
||||||
image={image}
|
image={image}
|
||||||
|
pretitle={pretitle}
|
||||||
title={title}
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
previousImageHref={previousIndex
|
previousImageHref={previousIndex
|
||||||
? getLocalizedUrl(`/collectibles/${slug}/gallery/${previousIndex}`)
|
? getLocalizedUrl(`/collectibles/${slug}/gallery/${previousIndex}`)
|
||||||
: undefined}
|
: undefined}
|
||||||
|
@ -35,8 +66,8 @@ const { title, description } =
|
||||||
? getLocalizedUrl(`/collectibles/${slug}/gallery/${nextIndex}`)
|
? getLocalizedUrl(`/collectibles/${slug}/gallery/${nextIndex}`)
|
||||||
: undefined}
|
: undefined}
|
||||||
description={description}
|
description={description}
|
||||||
filename={image.filename}
|
filename={filename}
|
||||||
tagGroups={image.tagGroups}
|
tagGroups={tagsAndAttributes}
|
||||||
credits={image.credits}
|
credits={credits}
|
||||||
/>
|
/>
|
||||||
</AppEmptyLayout>
|
</AppEmptyLayout>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||||
import Lightbox from "components/Lightbox.astro";
|
import Lightbox from "components/Lightbox.astro";
|
||||||
import { getI18n } from "src/i18n/i18n";
|
import { getI18n } from "src/i18n/i18n";
|
||||||
import { payload } from "src/shared/payload/payload-sdk";
|
import { payload } from "src/shared/payload/payload-sdk";
|
||||||
|
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||||
import { fetchOr404 } from "src/utils/responses";
|
import { fetchOr404 } from "src/utils/responses";
|
||||||
|
|
||||||
const { id } = Astro.params;
|
const { id } = Astro.params;
|
||||||
|
@ -11,24 +12,57 @@ if (image instanceof Response) {
|
||||||
return image;
|
return image;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
const { getLocalizedMatch, formatDate, t } = await getI18n(Astro.locals.currentLocale);
|
||||||
const { filename, translations, tagGroups, credits } = image;
|
const { filename, translations, tagGroups, credits, createdAt, updatedAt } = image;
|
||||||
|
|
||||||
const { title, description } =
|
const { pretitle, title, subtitle, description } =
|
||||||
translations.length > 0
|
translations.length > 0
|
||||||
? getLocalizedMatch(translations)
|
? 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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<AppEmptyLayout>
|
<AppEmptyLayout
|
||||||
|
openGraph={{
|
||||||
|
thumbnail: image,
|
||||||
|
description: description ? formatRichTextToString(description) : undefined,
|
||||||
|
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||||
|
}}>
|
||||||
<Lightbox
|
<Lightbox
|
||||||
image={image}
|
image={image}
|
||||||
|
pretitle={pretitle}
|
||||||
title={title}
|
title={title}
|
||||||
|
subtitle={subtitle}
|
||||||
description={description}
|
description={description}
|
||||||
filename={filename}
|
filename={filename}
|
||||||
tagGroups={tagGroups}
|
tagGroups={tagsAndAttributes}
|
||||||
credits={credits}
|
credits={credits}
|
||||||
/>
|
/>
|
||||||
</AppEmptyLayout>
|
</AppEmptyLayout>
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
---
|
---
|
||||||
import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
import AppEmptyLayout from "components/AppLayout/AppEmptyLayout.astro";
|
||||||
|
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
|
||||||
import Credits from "components/Credits.astro";
|
import Credits from "components/Credits.astro";
|
||||||
import DownloadButton from "components/DownloadButton.astro";
|
import DownloadButton from "components/DownloadButton.astro";
|
||||||
import RichText from "components/RichText/RichText.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 VideoPlayer from "components/VideoPlayer.astro";
|
||||||
import { getI18n } from "src/i18n/i18n";
|
import { getI18n } from "src/i18n/i18n";
|
||||||
import { payload } from "src/shared/payload/payload-sdk";
|
import { payload } from "src/shared/payload/payload-sdk";
|
||||||
|
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
|
||||||
import { fetchOr404 } from "src/utils/responses";
|
import { fetchOr404 } from "src/utils/responses";
|
||||||
|
|
||||||
const { id } = Astro.params;
|
const { id } = Astro.params;
|
||||||
|
@ -15,23 +17,80 @@ if (video instanceof Response) {
|
||||||
return video;
|
return video;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
|
const { getLocalizedMatch, t, formatFilesize, formatDate } = await getI18n(
|
||||||
const { translations, tagGroups, filename, url, credits } = video;
|
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 ------------------------------------------- */}
|
{/* ------------------------------------------- HTML ------------------------------------------- */}
|
||||||
|
|
||||||
<AppEmptyLayout>
|
<AppEmptyLayout
|
||||||
|
openGraph={{
|
||||||
|
thumbnail,
|
||||||
|
video,
|
||||||
|
description: description ? formatRichTextToString(description) : undefined,
|
||||||
|
title: formatInlineTitle({ pretitle, title, subtitle }),
|
||||||
|
}}>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<VideoPlayer video={video} />
|
<VideoPlayer video={video} />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
{
|
||||||
|
smallTitle ? (
|
||||||
<h1>{title}</h1>
|
<h1>{title}</h1>
|
||||||
|
) : (
|
||||||
|
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
|
||||||
|
)
|
||||||
|
}
|
||||||
{description && <RichText content={description} />}
|
{description && <RichText content={description} />}
|
||||||
<div>
|
<div>
|
||||||
{tagGroups.length > 0 && <TagGroups {tagGroups} />}
|
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
|
||||||
{credits.length > 0 && <Credits credits={credits} />}
|
{credits.length > 0 && <Credits credits={credits} />}
|
||||||
</div>
|
</div>
|
||||||
<DownloadButton href={url} filename={filename} />
|
<DownloadButton href={url} filename={filename} />
|
||||||
|
|
|
@ -111,7 +111,9 @@ export interface Image {
|
||||||
translations?:
|
translations?:
|
||||||
| {
|
| {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
|
pretitle?: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
description?: {
|
description?: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -559,7 +561,9 @@ export interface Audio {
|
||||||
thumbnail?: string | MediaThumbnail | null;
|
thumbnail?: string | MediaThumbnail | null;
|
||||||
translations: {
|
translations: {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
|
pretitle?: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
description?: {
|
description?: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -631,7 +635,9 @@ export interface Video {
|
||||||
thumbnail?: string | MediaThumbnail | null;
|
thumbnail?: string | MediaThumbnail | null;
|
||||||
translations: {
|
translations: {
|
||||||
language: string | Language;
|
language: string | Language;
|
||||||
|
pretitle?: string | null;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string | null;
|
||||||
description?: {
|
description?: {
|
||||||
root: {
|
root: {
|
||||||
type: string;
|
type: string;
|
||||||
|
@ -1711,7 +1717,9 @@ export type EndpointMedia = {
|
||||||
tagGroups: EndpointTagsGroup[];
|
tagGroups: EndpointTagsGroup[];
|
||||||
translations: {
|
translations: {
|
||||||
language: string;
|
language: string;
|
||||||
|
pretitle?: string;
|
||||||
title: string;
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
description?: RichTextContent;
|
description?: RichTextContent;
|
||||||
}[];
|
}[];
|
||||||
credits: EndpointCredit[];
|
credits: EndpointCredit[];
|
||||||
|
|
Loading…
Reference in New Issue