diff --git a/src/api/nitro/GetConfigurationValue.ts b/src/api/nitro/GetConfigurationValue.ts index 755ca1d..da063ff 100644 --- a/src/api/nitro/GetConfigurationValue.ts +++ b/src/api/nitro/GetConfigurationValue.ts @@ -4,3 +4,8 @@ export function GetConfigurationValue(key: string, value: T = null): { return GetConfiguration().getValue(key, value); } + +export function GetOptionalConfigurationValue(key: string, value: T = null): T +{ + return GetConfiguration().definitions.has(key) ? GetConfiguration().getValue(key, value) : value; +} diff --git a/src/components/login/LoginView.tsx b/src/components/login/LoginView.tsx index ce26fe6..cf88503 100644 --- a/src/components/login/LoginView.tsx +++ b/src/components/login/LoginView.tsx @@ -1,6 +1,6 @@ import { GetConfiguration } from '@nitrots/nitro-renderer'; import { FC, FormEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { ClearRememberLogin, GetConfigurationValue, GetRememberLogin, StoreRememberLoginFromPayload } from '../../api'; +import { ClearRememberLogin, GetConfigurationValue, GetOptionalConfigurationValue, GetRememberLogin, StoreRememberLoginFromPayload } from '../../api'; import { configFileUrl } from '../../secure-assets'; import flagBr from '../../assets/images/flag_icon/flag_icon_br.png'; import flagDe from '../../assets/images/flag_icon/flag_icon_de.png'; @@ -237,7 +237,8 @@ export const LoginView: FC = ({ onAuthenticated, isEntering = fa const loginUrl = GetConfigurationValue('login.endpoint', '/api/auth/login'); const registerUrl = GetConfigurationValue('login.register.endpoint', '/api/auth/register'); const forgotUrl = GetConfigurationValue('login.forgot.endpoint', '/api/auth/forgot-password'); - const newsUrl = interpolate(GetConfigurationValue('login.news.url', '')); + const configuredNewsUrl = interpolate(GetOptionalConfigurationValue('login.news.url', '')); + const newsUrl = configuredNewsUrl || configFileUrl('news.json'); const turnstileSiteKey = GetConfigurationValue('login.turnstile.sitekey', ''); const rawTurnstileEnabled = GetConfigurationValue('login.turnstile.enabled', false); const turnstileEnabled = (rawTurnstileEnabled === true @@ -366,7 +367,7 @@ export const LoginView: FC = ({ onAuthenticated, isEntering = fa }, []); const healthUrl = GetConfigurationValue('login.health.endpoint', ''); - const healthMethodRaw = GetConfigurationValue('login.health.method', 'GET'); + const healthMethodRaw = GetOptionalConfigurationValue('login.health.method', 'GET'); const healthMethod = (healthMethodRaw || 'GET').toUpperCase(); const checkServerReachable = useCallback(async (): Promise => { @@ -480,7 +481,7 @@ export const LoginView: FC = ({ onAuthenticated, isEntering = fa const checkEmailUrl = GetConfigurationValue('login.check-email.endpoint', '/api/auth/check-email'); const checkUsernameUrl = GetConfigurationValue('login.check-username.endpoint', '/api/auth/check-username'); - const imagingUrl = GetConfigurationValue('login.register.imaging.url', ''); + const imagingUrl = GetOptionalConfigurationValue('login.register.imaging.url', ''); const interpretAvailability = (ok: boolean, status: number, payload: Record): { available: boolean; error?: string } => { const isTrue = (v: unknown) => v === true || v === 'true' || v === 1 || v === '1'; diff --git a/src/components/login/components/NewsWindow.tsx b/src/components/login/components/NewsWindow.tsx index a7c05d3..d643731 100644 --- a/src/components/login/components/NewsWindow.tsx +++ b/src/components/login/components/NewsWindow.tsx @@ -1,5 +1,5 @@ import { FC, useEffect, useState } from 'react'; -import { t } from '../utils/i18n'; +import { interpolate, t } from '../utils/i18n'; import { resolveNewsImage, resolveNewsLink } from '../utils/news'; interface NewsItem @@ -12,6 +12,26 @@ interface NewsItem linkUrl: string; } +interface RawNewsItem +{ + id?: number; + title?: string; + body?: string; + image?: string | null; + link?: string; + linkUrl?: string; + linkText?: string; +} + +const normalizeNewsItem = (raw: RawNewsItem, fallbackId: number): NewsItem => ({ + id: typeof raw.id === 'number' ? raw.id : fallbackId, + title: typeof raw.title === 'string' ? raw.title : '', + body: typeof raw.body === 'string' ? raw.body : '', + image: typeof raw.image === 'string' && raw.image.length ? interpolate(raw.image) : null, + linkText: typeof raw.linkText === 'string' ? raw.linkText : '', + linkUrl: interpolate((typeof raw.linkUrl === 'string' && raw.linkUrl) || (typeof raw.link === 'string' ? raw.link : '')) +}); + interface NewsWindowProps { newsUrl: string; } const NEWS_AUTO_ADVANCE_MS = 10000; @@ -36,10 +56,10 @@ export const NewsWindow: FC = ({ newsUrl }) => .then((json: unknown) => { if(cancelled) return; - const list = Array.isArray((json as { news?: unknown })?.news) - ? (json as { news: NewsItem[] }).news - : []; - setItems(list); + const rawList = Array.isArray((json as { news?: unknown })?.news) + ? (json as { news: RawNewsItem[] }).news + : Array.isArray(json) ? (json as RawNewsItem[]) : []; + setItems(rawList.map((raw, idx) => normalizeNewsItem(raw, idx + 1))); }) .catch(() => { if(!cancelled) setFailed(true); }); return () => { cancelled = true; };