diff --git a/app/features/art/components/ArtGrid.module.css b/app/features/art/components/ArtGrid.module.css index f09aabdb3..42321780f 100644 --- a/app/features/art/components/ArtGrid.module.css +++ b/app/features/art/components/ArtGrid.module.css @@ -56,6 +56,34 @@ ); } +/* nothing to look at, so the details take over the figure instead of floating over the image */ +.lightboxFigureFailed { + display: flex; + flex-direction: column; + width: min(var(--lightbox-max-width), 22rem); + background-color: rgba(0, 0, 0, 0.6); + + .lightboxInfo { + position: static; + opacity: 1; + padding-block-start: var(--s-3); + background: none; + } +} + +.lightboxFailed { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: var(--s-2); + min-height: 10rem; + padding: var(--s-4); + color: rgba(255, 255, 255, 0.7); + font-size: var(--font-sm); + text-align: center; +} + .lightboxPlaceholder { display: block; width: 100%; diff --git a/app/features/art/components/ArtGrid.tsx b/app/features/art/components/ArtGrid.tsx index 5d7b8bb7c..b1480acc9 100644 --- a/app/features/art/components/ArtGrid.tsx +++ b/app/features/art/components/ArtGrid.tsx @@ -1,5 +1,5 @@ import clsx from "clsx"; -import { SquarePen, Trash, Unlink } from "lucide-react"; +import { ImageOff, SquarePen, Trash, Unlink } from "lucide-react"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router"; @@ -90,12 +90,15 @@ function BigImageDialog({ close, art }: { close: () => void; art: ListedArt }) { const [infoVisible, setInfoVisible] = React.useState(true); const [imageSettled, imageRef] = useImageSettled(); const [aspectRatio, placeholderRef] = useImageAspectRatio(); + const { t } = useTranslation(["art"]); const { formatter } = useDateTimeFormat({ year: "numeric", month: "numeric", day: "numeric", }); + const imageFailed = aspectRatio === "FAILED"; + const dateText = formatter.format(databaseTimestampToDate(art.createdAt)); const handleClick = (event: React.MouseEvent) => { @@ -120,10 +123,11 @@ function BigImageDialog({ close, art }: { close: () => void; art: ListedArt }) {
void; art: ListedArt }) { className={styles.lightboxPlaceholder} ref={placeholderRef} /> - + {imageFailed ? ( +
+ + {t("art:imageFailed")} +
+ ) : ( + + )}
(null); + const [aspectRatio, setAspectRatio] = React.useState< + number | "FAILED" | null + >(null); const imageRef = (image: HTMLImageElement | null) => { if (!image) return; const measure = () => { - if (image.naturalWidth > 0 && image.naturalHeight > 0) { - setAspectRatio(image.naturalWidth / image.naturalHeight); - } + setAspectRatio( + image.naturalWidth > 0 && image.naturalHeight > 0 + ? image.naturalWidth / image.naturalHeight + : "FAILED", + ); }; if (image.complete) { measure(); @@ -382,7 +400,12 @@ function useImageAspectRatio() { } image.addEventListener("load", measure); - return () => image.removeEventListener("load", measure); + image.addEventListener("error", measure); + + return () => { + image.removeEventListener("load", measure); + image.removeEventListener("error", measure); + }; }; return [aspectRatio, imageRef] as const; diff --git a/locales/da/art.json b/locales/da/art.json index 11d7851b0..b5349fc3d 100644 --- a/locales/da/art.json +++ b/locales/da/art.json @@ -22,5 +22,6 @@ "forms.tags.maxReached": "Maks antal etiketter opnået", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/de/art.json b/locales/de/art.json index baf5e611f..852565d8a 100644 --- a/locales/de/art.json +++ b/locales/de/art.json @@ -22,5 +22,6 @@ "forms.tags.maxReached": "", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/en/art.json b/locales/en/art.json index 3b12a358a..655b8117d 100644 --- a/locales/en/art.json +++ b/locales/en/art.json @@ -22,5 +22,6 @@ "forms.tags.maxReached": "Max tags reached", "delete.title": "Are you sure you want to delete the art?", "unlink.title": "Are you sure you want to remove this art from your profile (only {{username}} can add it back)?", - "noArtForTag": "No uploaded art found for #{{tag}}" + "noArtForTag": "No uploaded art found for #{{tag}}", + "imageFailed": "Image could not be loaded" } diff --git a/locales/es-ES/art.json b/locales/es-ES/art.json index 7efc1e9a9..a83b4598f 100644 --- a/locales/es-ES/art.json +++ b/locales/es-ES/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Límite máximo de etiquetas", "delete.title": "¿Seguro que quieres eliminar este arte?", "unlink.title": "¿Seguro que quieres desvincular este arte de tu perfil? (solo {{username}} puede volver a añadirlo)", - "noArtForTag": "No se ha encontrado arte subido para #{{tag}}" + "noArtForTag": "No se ha encontrado arte subido para #{{tag}}", + "imageFailed": "" } diff --git a/locales/es-US/art.json b/locales/es-US/art.json index 8b760f074..f1756fdb5 100644 --- a/locales/es-US/art.json +++ b/locales/es-US/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Límite máximo de etiquetas", "delete.title": "¿Seguro que quieres eliminar este arte?", "unlink.title": "¿Seguro que quieres desvincular este arte de tu perfil? (solo {{username}} puede volver a añadirlo)", - "noArtForTag": "No se ha encontrado arte subido para #{{tag}}" + "noArtForTag": "No se ha encontrado arte subido para #{{tag}}", + "imageFailed": "" } diff --git a/locales/fr-CA/art.json b/locales/fr-CA/art.json index 1d5f7a4cf..9f38265a1 100644 --- a/locales/fr-CA/art.json +++ b/locales/fr-CA/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Limite de tags atteinte", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/fr-EU/art.json b/locales/fr-EU/art.json index 2208b153d..10328c217 100644 --- a/locales/fr-EU/art.json +++ b/locales/fr-EU/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Limite de tags atteinte", "delete.title": "Êtes vous sûre de vouloir supprimer cette œuvre?", "unlink.title": "Êtes vous sûre de vouloir supprimer cette œuvre de votre profil? (Seulement {{username}} pourra le remettre)", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/he/art.json b/locales/he/art.json index 0f5e17ab3..355a72d8e 100644 --- a/locales/he/art.json +++ b/locales/he/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "הגעת לכמות תגים מקסימלית", "delete.title": "האם אתה בטוח שאתה רוצה למחוק את הציור?", "unlink.title": "האם אתה בטוח שאתה רוצה להסיר את הציור הזה מהפרופיל שלך (רק {{username}} יוכל להוסיף אותו חזרה)?", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/it/art.json b/locales/it/art.json index 3adb73671..bab969f02 100644 --- a/locales/it/art.json +++ b/locales/it/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Numero massimo di tag raggiunto.", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/ja/art.json b/locales/ja/art.json index 6b5cd67c6..707c457b4 100644 --- a/locales/ja/art.json +++ b/locales/ja/art.json @@ -20,5 +20,6 @@ "forms.tags.maxReached": "タグの最大数に到達しました", "delete.title": "本当にこの作品を削除しますか?", "unlink.title": "本当にこの作品をプロフィールから削除しますか?{{username}}しか再び追加することはできません。", - "noArtForTag": "#{{tag}}の作品が見つかりませんでした。" + "noArtForTag": "#{{tag}}の作品が見つかりませんでした。", + "imageFailed": "" } diff --git a/locales/ko/art.json b/locales/ko/art.json index 0136021ce..654f7c885 100644 --- a/locales/ko/art.json +++ b/locales/ko/art.json @@ -20,5 +20,6 @@ "forms.tags.maxReached": "", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/nl/art.json b/locales/nl/art.json index baf5e611f..852565d8a 100644 --- a/locales/nl/art.json +++ b/locales/nl/art.json @@ -22,5 +22,6 @@ "forms.tags.maxReached": "", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/pl/art.json b/locales/pl/art.json index 86b280742..f801affdc 100644 --- a/locales/pl/art.json +++ b/locales/pl/art.json @@ -24,5 +24,6 @@ "forms.tags.maxReached": "", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/pt-BR/art.json b/locales/pt-BR/art.json index 21533fb0a..303786be2 100644 --- a/locales/pt-BR/art.json +++ b/locales/pt-BR/art.json @@ -23,5 +23,6 @@ "forms.tags.maxReached": "Quantidade máxima de marcações atingida", "delete.title": "", "unlink.title": "", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/ru/art.json b/locales/ru/art.json index f0fc40155..08a0d70a6 100644 --- a/locales/ru/art.json +++ b/locales/ru/art.json @@ -24,5 +24,6 @@ "forms.tags.maxReached": "Максимальное кол-во тегов достигнуто", "delete.title": "Вы точно хотите удалить этот арт?", "unlink.title": "Вы точно хотите удалить этот арт с вашего профиля (только {{username}} может добавить его обратно)?", - "noArtForTag": "" + "noArtForTag": "", + "imageFailed": "" } diff --git a/locales/zh/art.json b/locales/zh/art.json index 4a0145b04..af36e7d61 100644 --- a/locales/zh/art.json +++ b/locales/zh/art.json @@ -21,5 +21,6 @@ "forms.tags.maxReached": "已达到最大标签数", "delete.title": "您确定要删除这件作品吗?", "unlink.title": "您确定要将这件作品从个人资料中移除吗(只有 {{username}} 可以重新添加)?", - "noArtForTag": "未能找到包含 #{{tag}} 的作品。" + "noArtForTag": "未能找到包含 #{{tag}} 的作品。", + "imageFailed": "" }