feat(gallery): always-visible feedback indicators on grid tiles; fallback image rendering in lightbox/hero; auto-auth from shared-link token; fix external photo resolver\n\n- GridGallery: bottom-left icons for like/rated/comment on every tile\n- Hero layout grid: added same indicators (non-intrusive icons)\n- Lightbox/Hero: add fallbackSrc to display thumbnail if original fails\n- GalleryAuth: auto-store token from /gallery/:slug/:token and hydrate event\n- Backend gallery photo route: use resolvePhotoFilePath for external-media\n\nfix(admin): move photo feedback badges to bottom-right on admin grid tiles\n\nfix(dashboard): add missing i18n keys for activity types + fallback to formatter\n\nfix(admin/feedback): correct thumbnail URL base + robust date parsing\n\nRefs: #19

This commit is contained in:
2025-09-15 22:55:35 +02:00
parent 4c7b49a5f6
commit 6948aaa92a
13 changed files with 143 additions and 50 deletions
+31 -2
View File
@@ -1,6 +1,6 @@
import React, { createContext, useContext, useState, useEffect } from 'react';
import type { ReactNode } from 'react';
import { authService } from '../services';
import { authService, galleryService } from '../services';
import { cleanupOldGalleryAuth } from '../utils/cleanupGalleryAuth';
interface GalleryEvent {
@@ -79,6 +79,35 @@ export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ childr
localStorage.removeItem(`gallery_event_${currentSlug}`);
localStorage.removeItem(`gallery_token_${currentSlug}`);
}
} else {
// No stored auth; check for token in URL and auto-authenticate
const parts = window.location.pathname.split('/');
const urlToken = parts.length >= 5 ? parts[4] : (parts.length >= 4 ? parts[3] : undefined);
if (urlToken) {
(async () => {
try {
setIsLoading(true);
// Verify token against backend
const verify = await galleryService.verifyToken(currentSlug, urlToken);
if (verify?.valid) {
// Store token and fetch event via photos endpoint to get full event object
localStorage.setItem(`gallery_token_${currentSlug}`, urlToken);
const data = await galleryService.getGalleryPhotos(currentSlug);
if (data?.event) {
setEvent(data.event);
setIsAuthenticated(true);
localStorage.setItem(`gallery_event_${currentSlug}`, JSON.stringify(data.event));
}
}
} catch (e) {
// Invalid token; ensure any residual storage is cleared
localStorage.removeItem(`gallery_token_${currentSlug}`);
localStorage.removeItem(`gallery_event_${currentSlug}`);
} finally {
setIsLoading(false);
}
})();
}
}
}
setIsLoading(false);
@@ -128,4 +157,4 @@ export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ childr
{children}
</GalleryAuthContext.Provider>
);
};
};