fix(gallery): update image count translations and improve size calculation logic

This commit is contained in:
lzinga 2025-11-28 16:13:52 -08:00
parent 4779492bb7
commit 0a860ec963
2 changed files with 26 additions and 47 deletions

View File

@ -2113,8 +2113,8 @@
"no_images": "No Images", "no_images": "No Images",
"upload_first_image": "Upload First Image", "upload_first_image": "Upload First Image",
"image_count": "{{count}} Image", "image_count": "{{count}} Image",
"image_count_other": "{{count}} Images",
"upload_images": "Upload Images", "upload_images": "Upload Images",
"image_count_plural": "{{count}} Images",
"confirm_delete_note": "Are you sure you want to delete {{title}}", "confirm_delete_note": "Are you sure you want to delete {{title}}",
"confirm_delete_attachment": "Are you sure you want to delete the attachment {{title}}", "confirm_delete_attachment": "Are you sure you want to delete the attachment {{title}}",
"delete_note_success": "{{title}} deleted successfully", "delete_note_success": "{{title}} deleted successfully",
@ -2127,7 +2127,8 @@
"select": "Select Multiple", "select": "Select Multiple",
"select_all": "Select All", "select_all": "Select All",
"deselect_all": "Deselect All", "deselect_all": "Deselect All",
"delete_selected": "Delete {{count}} Items", "delete_selected": "Delete {{count}} Item",
"delete_selected_other": "Delete {{count}} Items",
"delete": "Delete", "delete": "Delete",
"previous": "Previous", "previous": "Previous",
"next": "Next", "next": "Next",
@ -2143,6 +2144,7 @@
"toggle_child_images_tooltip": "Toggle Child Images", "toggle_child_images_tooltip": "Toggle Child Images",
"not_shared": "The gallery is not shared", "not_shared": "The gallery is not shared",
"shared_success": "The gallery is now being shared", "shared_success": "The gallery is now being shared",
"unshared_success": "The gallery has stopped being shared" "unshared_success": "The gallery has stopped being shared",
"image_not_found": "Image not found"
} }
} }

View File

@ -1,8 +1,6 @@
import { TypeWidgetProps } from "./type_widget"; import { TypeWidgetProps } from "./type_widget";
import { useContext, useEffect, useRef, useState } from "preact/hooks"; import { useContext, useEffect, useState } from "preact/hooks";
import { useTriliumEvent, useTriliumOption } from "../react/hooks"; import { useTriliumEvent, useTriliumOption } from "../react/hooks";
import FNote from "../../entities/fnote";
import FAttachment from "../../entities/fattachment";
import { ParentComponent } from "../react/react_utils"; import { ParentComponent } from "../react/react_utils";
import Button from "../react/Button"; import Button from "../react/Button";
import { t } from "../../services/i18n"; import { t } from "../../services/i18n";
@ -38,7 +36,6 @@ export default function Gallery({ note }: TypeWidgetProps) {
async function loadImages() { async function loadImages() {
const imageItems: ImageItem[] = []; const imageItems: ImageItem[] = [];
let calculatedTotalSize = 0;
// Check if the gallery note itself is shared // Check if the gallery note itself is shared
const galleryIsShared = note.hasAncestor("_share"); const galleryIsShared = note.hasAncestor("_share");
@ -53,16 +50,13 @@ export default function Gallery({ note }: TypeWidgetProps) {
// Process direct attachments // Process direct attachments
for (const attachment of directImageAttachments) { for (const attachment of directImageAttachments) {
const size = attachment.contentLength || 0;
calculatedTotalSize += size;
imageItems.push({ imageItems.push({
id: attachment.attachmentId, id: attachment.attachmentId,
url: `api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}`, url: `api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}`,
title: attachment.title, title: attachment.title,
type: 'attachment' as const, type: 'attachment' as const,
noteId: attachment.ownerId, noteId: attachment.ownerId,
size size: attachment.contentLength || 0
}); });
} }
@ -76,7 +70,6 @@ export default function Gallery({ note }: TypeWidgetProps) {
const imageBlobPromises = imageNotes.map(async (imageNote) => { const imageBlobPromises = imageNotes.map(async (imageNote) => {
const blob = await imageNote.getBlob(); const blob = await imageNote.getBlob();
const size = blob?.contentLength || 0; const size = blob?.contentLength || 0;
calculatedTotalSize += size;
return { return {
id: imageNote.noteId, id: imageNote.noteId,
@ -90,45 +83,33 @@ export default function Gallery({ note }: TypeWidgetProps) {
const imageNoteItems = await Promise.all(imageBlobPromises); const imageNoteItems = await Promise.all(imageBlobPromises);
imageItems.push(...imageNoteItems); imageItems.push(...imageNoteItems);
// Recalculate total size from image note items
imageNoteItems.forEach(item => {
calculatedTotalSize += item.size || 0;
});
// Process child note attachments in parallel // Process child note attachments in parallel
const attachmentPromises = childNotes.map(async (childNote) => { const attachmentPromises = childNotes.map(async (childNote) => {
const attachments = await childNote.getAttachments(); const attachments = await childNote.getAttachments();
const imageAttachments = attachments.filter(a => a.role === "image"); const imageAttachments = attachments.filter(a => a.role === "image");
return imageAttachments.map(attachment => { return imageAttachments.map(attachment => ({
const size = attachment.contentLength || 0;
return {
id: attachment.attachmentId, id: attachment.attachmentId,
url: `api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}`, url: `api/attachments/${attachment.attachmentId}/image/${encodeURIComponent(attachment.title)}`,
title: attachment.title, title: attachment.title,
type: 'attachment' as const, type: 'attachment' as const,
noteId: attachment.ownerId, noteId: attachment.ownerId,
size size: attachment.contentLength || 0
}; }));
});
}); });
const attachmentArrays = await Promise.all(attachmentPromises); const attachmentArrays = await Promise.all(attachmentPromises);
const allAttachments = attachmentArrays.flat(); const allAttachments = attachmentArrays.flat();
imageItems.push(...allAttachments); imageItems.push(...allAttachments);
// Calculate total size from attachments
allAttachments.forEach(item => {
calculatedTotalSize += item.size || 0;
});
} }
// Calculate total size once at the end from all items
const calculatedTotalSize = imageItems.reduce((sum, item) => sum + (item.size || 0), 0);
setImages(imageItems); setImages(imageItems);
setTotalSize(calculatedTotalSize); setTotalSize(calculatedTotalSize);
} }
useEffect(() => { useEffect(() => {
loadImages(); loadImages();
}, [note]); }, [note]);
@ -175,7 +156,6 @@ export default function Gallery({ note }: TypeWidgetProps) {
// Note: No else clause - we don't do anything for gallery note attachments // Note: No else clause - we don't do anything for gallery note attachments
} }
// Your current handleCopyImageLink is actually fine for both Electron and web
async function handleCopyImageLink(img: ImageItem, e: MouseEvent) { async function handleCopyImageLink(img: ImageItem, e: MouseEvent) {
e.stopPropagation(); e.stopPropagation();
@ -188,7 +168,10 @@ export default function Gallery({ note }: TypeWidgetProps) {
if (img.type === 'note') { if (img.type === 'note') {
const imageNote = froca.getNoteFromCache(img.id); const imageNote = froca.getNoteFromCache(img.id);
if (!imageNote) return; if (!imageNote) {
toast.showError(t("gallery.image_not_found"));
return;
}
const shareId = imageNote.getOwnedLabelValue("shareAlias") || img.id; const shareId = imageNote.getOwnedLabelValue("shareAlias") || img.id;
imageUrl = getAbsoluteUrl(`/share/api/images/${shareId}/${encodeURIComponent(img.title)}`); imageUrl = getAbsoluteUrl(`/share/api/images/${shareId}/${encodeURIComponent(img.title)}`);
@ -230,13 +213,10 @@ export default function Gallery({ note }: TypeWidgetProps) {
function getAbsoluteUrl(path: string): string { function getAbsoluteUrl(path: string): string {
if (syncServerHost) { if (syncServerHost) {
return new URL(path, syncServerHost).href; return new URL(path, syncServerHost).href;
} else {
let host = location.host;
if (host.endsWith("/")) {
host = host.substring(0, host.length - 1);
}
return `${location.protocol}//${host}${location.pathname.replace(/\/$/, '')}${path}`;
} }
const origin = `${location.protocol}//${location.host}`;
const pathname = location.pathname.replace(/\/$/, '');
return `${origin}${pathname}${path}`;
} }
function toggleImageSelection(imageId: string) { function toggleImageSelection(imageId: string) {
@ -443,10 +423,7 @@ export default function Gallery({ note }: TypeWidgetProps) {
)} )}
<span className="gallery-count"> <span className="gallery-count">
{images.length === 1 {t("gallery.image_count", { count: images.length })}
? t("gallery.image_count", { count: images.length })
: t("gallery.image_count_plural", { count: images.length })
}
{totalSize > 0 && ` (${formatSize(totalSize)})`} {totalSize > 0 && ` (${formatSize(totalSize)})`}
</span> </span>
</div> </div>