import { GetConfiguration, GetLocalizationManager } from '@nitrots/nitro-renderer'; import { getAccessToken } from '../auth'; export interface CustomBadgeRecord { badgeId: string; badgeCode: string; name: string; description: string; dateCreated: number; dateEdit: number; url: string; } export interface CustomBadgeListResponse { badges: CustomBadgeRecord[]; max: number; badgeWidth: number; badgeHeight: number; maxBadgeSizeBytes: number; priceBadge?: number; currencyType?: number; } export interface CustomBadgeError { error: string; code?: string; } const interpolate = (value: string): string => { try { return GetConfiguration().interpolate(value); } catch { return value; } }; const getConfigUrl = (key: string, fallback: string): string => interpolate(GetConfiguration().getValue(key, fallback)); const buildUrl = (key: string, fallback: string, badgeId?: string): string => { const template = getConfigUrl(key, fallback); if(!badgeId) return template; if(template.includes('%badgeId%')) return template.replace(/%badgeId%/g, encodeURIComponent(badgeId)); return template + (template.endsWith('/') ? '' : '/') + encodeURIComponent(badgeId); }; const authHeaders = (): Record => { const headers: Record = { 'Accept': 'application/json', 'X-Requested-With': 'NitroCustomBadges' }; const token = getAccessToken(); if(token) headers['Authorization'] = `Bearer ${ token }`; return headers; }; const parseJson = async (response: Response): Promise => { const text = await response.text(); if(!text) return {} as T; try { return JSON.parse(text) as T; } catch { throw new Error('Invalid response from server.'); } }; const throwOnError = async (response: Response): Promise => { if(response.ok) return; const payload = await parseJson(response); const message = payload?.error || `Request failed (${ response.status }).`; const err = new Error(message) as Error & { status: number; code?: string }; err.status = response.status; if(payload?.code) err.code = payload.code; throw err; }; export const fetchCustomBadges = async (): Promise => { const url = buildUrl('badges.custom.list.endpoint', '/api/badges/custom'); const response = await fetch(url, { method: 'GET', credentials: 'include', headers: authHeaders() }); await throwOnError(response); return parseJson(response); }; export const createCustomBadge = async (body: { name: string; description: string; image: string }): Promise => { const url = buildUrl('badges.custom.create.endpoint', '/api/badges/custom'); const response = await fetch(url, { method: 'POST', credentials: 'include', headers: { ...authHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); await throwOnError(response); return parseJson(response); }; export const updateCustomBadge = async (badgeId: string, body: { name: string; description: string; image: string }): Promise => { const url = buildUrl('badges.custom.update.endpoint', '/api/badges/custom/%badgeId%', badgeId); const response = await fetch(url, { method: 'PUT', credentials: 'include', headers: { ...authHeaders(), 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); await throwOnError(response); return parseJson(response); }; export const deleteCustomBadge = async (badgeId: string): Promise => { const url = buildUrl('badges.custom.delete.endpoint', '/api/badges/custom/%badgeId%', badgeId); const response = await fetch(url, { method: 'DELETE', credentials: 'include', headers: authHeaders() }); await throwOnError(response); }; export const isCustomBadgeCode = (code: string | null | undefined): boolean => { if(!code) return false; return /^CUST[A-Z0-9]{5}-\d+$/.test(code); }; let customBadgeTextsLoadPromise: Promise | null = null; const injectTextsIntoLocalization = (texts: Record | null | undefined): void => { if(!texts) return; let manager: ReturnType | null = null; try { manager = GetLocalizationManager(); } catch { return; } if(!manager || typeof manager.setValue !== 'function') return; for(const key of Object.keys(texts)) { const value = texts[key]; if(typeof value === 'string') manager.setValue(key, value); } }; export const ensureCustomBadgeTexts = (): Promise => { if(customBadgeTextsLoadPromise) return customBadgeTextsLoadPromise; customBadgeTextsLoadPromise = (async () => { try { const url = buildUrl('badges.custom.texts.endpoint', '/api/badges/custom/texts'); const response = await fetch(url, { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }); if(!response.ok) return; const payload = await parseJson<{ texts: Record }>(response); injectTextsIntoLocalization(payload.texts); } catch {} })(); return customBadgeTextsLoadPromise; }; export const refreshCustomBadgeTexts = (): Promise => { customBadgeTextsLoadPromise = null; return ensureCustomBadgeTexts(); }; export const setCustomBadgeText = (badgeId: string, name: string, description: string): void => { injectTextsIntoLocalization({ [`badge_name_${ badgeId }`]: name || badgeId, [`badge_desc_${ badgeId }`]: description || '' }); };