feat: localize guest endpoints and caching
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { getDeviceId } from '../lib/device';
|
||||
import { DEFAULT_LOCALE } from '../i18n/messages';
|
||||
|
||||
export interface AchievementBadge {
|
||||
id: string;
|
||||
@@ -86,25 +87,60 @@ function safeString(value: unknown): string {
|
||||
return typeof value === 'string' ? value : '';
|
||||
}
|
||||
|
||||
type FetchAchievementsOptions = {
|
||||
guestName?: string;
|
||||
locale?: string;
|
||||
signal?: AbortSignal;
|
||||
forceRefresh?: boolean;
|
||||
};
|
||||
|
||||
type AchievementsCacheEntry = {
|
||||
data: AchievementsPayload;
|
||||
etag: string | null;
|
||||
};
|
||||
|
||||
const achievementsCache = new Map<string, AchievementsCacheEntry>();
|
||||
|
||||
export async function fetchAchievements(
|
||||
eventToken: string,
|
||||
guestName?: string,
|
||||
signal?: AbortSignal
|
||||
options: FetchAchievementsOptions = {}
|
||||
): Promise<AchievementsPayload> {
|
||||
const { guestName, signal, forceRefresh } = options;
|
||||
const locale = options.locale ?? DEFAULT_LOCALE;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
if (guestName && guestName.trim().length > 0) {
|
||||
params.set('guest_name', guestName.trim());
|
||||
}
|
||||
if (locale) {
|
||||
params.set('locale', locale);
|
||||
}
|
||||
|
||||
const deviceId = getDeviceId();
|
||||
const cacheKey = [eventToken, locale, guestName?.trim() ?? '', deviceId].join(':');
|
||||
const cached = forceRefresh ? null : achievementsCache.get(cacheKey);
|
||||
|
||||
const headers: HeadersInit = {
|
||||
'X-Device-Id': deviceId,
|
||||
'Cache-Control': 'no-store',
|
||||
Accept: 'application/json',
|
||||
'X-Locale': locale,
|
||||
};
|
||||
|
||||
if (cached?.etag) {
|
||||
headers['If-None-Match'] = cached.etag;
|
||||
}
|
||||
|
||||
const response = await fetch(`/api/v1/events/${encodeURIComponent(eventToken)}/achievements?${params.toString()}`, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'X-Device-Id': getDeviceId(),
|
||||
'Cache-Control': 'no-store',
|
||||
},
|
||||
headers,
|
||||
signal,
|
||||
});
|
||||
|
||||
if (response.status === 304 && cached) {
|
||||
return cached.data;
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
const message = await response.text();
|
||||
throw new Error(message || 'Achievements request failed');
|
||||
@@ -190,7 +226,7 @@ export async function fetchAchievements(
|
||||
thumbnail: row.thumbnail ? safeString(row.thumbnail) : null,
|
||||
}));
|
||||
|
||||
return {
|
||||
const payload: AchievementsPayload = {
|
||||
summary: {
|
||||
totalPhotos: toNumber(summary.total_photos),
|
||||
uniqueGuests: toNumber(summary.unique_guests),
|
||||
@@ -209,4 +245,11 @@ export async function fetchAchievements(
|
||||
},
|
||||
feed,
|
||||
};
|
||||
|
||||
achievementsCache.set(cacheKey, {
|
||||
data: payload,
|
||||
etag: response.headers.get('ETag'),
|
||||
});
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user