Add mobile image display and optimize gallery loading

Update gallery component to conditionally load mobile-optimized images for smaller screens and refactor Dropbox integration to fetch images from separate desktop and mobile folders.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 1f7e7e89-a520-4970-9645-37daadc466dc
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: 047c052f-7045-4b74-a78d-c75e84aaddd0
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/f209e72a-0939-48fa-84fc-57854de71967/1f7e7e89-a520-4970-9645-37daadc466dc/5JDNRWP
Replit-Helium-Checkpoint-Created: true
This commit is contained in:
sebastjanartic
2026-03-05 14:03:51 +00:00
parent 23149965d7
commit dcf8512631
4 changed files with 628 additions and 1151 deletions
+48 -30
View File
@@ -17,6 +17,7 @@ interface GalleryImage {
fileName: string;
thumb: string;
large: string;
mobile: string;
full: string;
artist: string;
}
@@ -215,6 +216,10 @@ async function getTemporaryLinks(accessToken: string, paths: string[]): Promise<
const galleryCache: { data: GalleryImage[]; timestamp: number } = { data: [], timestamp: 0 };
const CACHE_DURATION = 30 * 60 * 1000;
const THUMB_FOLDER = GALLERY_ROOT + "/Foto 1x1";
const LARGE_FOLDER = GALLERY_ROOT + "/Foto 9x16";
const MOBILE_FOLDER = GALLERY_ROOT + "/Foto 16x9";
export async function fetchGalleryFromDropbox(): Promise<GalleryImage[]> {
if (galleryCache.data.length > 0 && Date.now() - galleryCache.timestamp < CACHE_DURATION) {
return galleryCache.data;
@@ -224,37 +229,50 @@ export async function fetchGalleryFromDropbox(): Promise<GalleryImage[]> {
if (!accessToken) return [];
try {
const topEntries = await listFolder(accessToken, GALLERY_ROOT);
const folders = topEntries.filter((e: any) => e[".tag"] === "folder");
const [thumbEntries, largeEntries, mobileEntries] = await Promise.all([
listFolder(accessToken, THUMB_FOLDER.toLowerCase()),
listFolder(accessToken, LARGE_FOLDER.toLowerCase()),
listFolder(accessToken, MOBILE_FOLDER.toLowerCase()),
]);
const imgFilter = (e: any) => e[".tag"] === "file" && /\.(jpg|jpeg|png|webp|gif)$/i.test(e.name);
const thumbFiles = thumbEntries.filter(imgFilter);
const largeFiles = largeEntries.filter(imgFilter);
const mobileFiles = mobileEntries.filter(imgFilter);
const [thumbLinks, largeLinks, mobileLinks] = await Promise.all([
getTemporaryLinks(accessToken, thumbFiles.map((f: any) => f.path_lower)),
getTemporaryLinks(accessToken, largeFiles.map((f: any) => f.path_lower)),
getTemporaryLinks(accessToken, mobileFiles.map((f: any) => f.path_lower)),
]);
const largeMap = new Map<string, string>();
for (const file of largeFiles) {
const link = largeLinks.get(file.path_lower) || "";
if (link) largeMap.set(file.name.toLowerCase(), link);
}
const mobileMap = new Map<string, string>();
for (const file of mobileFiles) {
const link = mobileLinks.get(file.path_lower) || "";
if (link) mobileMap.set(file.name.toLowerCase(), link);
}
const images: GalleryImage[] = [];
for (const folder of folders) {
try {
const folderName = folder.name;
const folderEntries = await listFolder(accessToken, folder.path_lower);
const imageFiles = folderEntries.filter(
(e: any) => e[".tag"] === "file" && /\.(jpg|jpeg|png|webp|gif)$/i.test(e.name)
);
const paths = imageFiles.map((f: any) => f.path_lower);
const tempLinks = await getTemporaryLinks(accessToken, paths);
for (const file of imageFiles) {
const link = tempLinks.get(file.path_lower) || "";
if (link) {
images.push({
folder: folderName,
fileName: file.name,
thumb: link,
large: link,
full: link,
artist: extractArtistFromFileName(file.name),
});
}
}
} catch (err: any) {
console.error(`Folder ${folder.name} error:`, err.message);
}
for (const file of thumbFiles) {
const thumbLink = thumbLinks.get(file.path_lower) || "";
if (!thumbLink) continue;
const largeLink = largeMap.get(file.name.toLowerCase()) || thumbLink;
const mobileLink = mobileMap.get(file.name.toLowerCase()) || thumbLink;
images.push({
folder: "Foto All",
fileName: file.name,
thumb: thumbLink,
large: largeLink,
mobile: mobileLink,
full: largeLink,
artist: extractArtistFromFileName(file.name),
});
}
if (images.length > 0) {
+565 -1100
View File
File diff suppressed because it is too large Load Diff
+10 -17
View File
@@ -353,26 +353,19 @@ export async function registerRoutes(
}
});
// Gallery API - serves photos from Cloudinary (gallery-data.json)
const folderArtists: Record<string, string> = {
"2": "Folx Stadl",
"3": "Folx Stadl",
"4": "Folx Stadl",
"5": "Folx Stadl",
"6": "Folx Stadl",
};
// Gallery API - serves optimized photos from Dropbox
app.get("/api/gallery", async (req, res) => {
try {
const galleryPath = path.join(process.cwd(), "server/gallery-data.json");
let data: any[] = JSON.parse(fs.readFileSync(galleryPath, "utf-8"));
let data: any[] = [];
data = data.map((img: any) => {
if (!img.artist && folderArtists[img.folder]) {
return { ...img, artist: folderArtists[img.folder] };
}
return img;
});
if (isConnected()) {
data = await fetchGalleryFromDropbox();
}
if (data.length === 0) {
const galleryPath = path.join(process.cwd(), "server/gallery-data.json");
data = JSON.parse(fs.readFileSync(galleryPath, "utf-8"));
}
const shuffled = [...data];
for (let i = shuffled.length - 1; i > 0; i--) {