feat(a11y,i18n): more modal focus-traps + Telegram theme/language sync

Modal a11y:
- focus-trap + role=dialog/aria-modal/aria-labelledby on AdminRoles delete confirm
  and AdminBanSystem user-detail modal (+ close aria-label)

Telegram theme/language sync (first run only, explicit choice always wins):
- getTelegramColorScheme() / getTelegramLanguageCode() helpers in useTelegramSDK
- useTheme initial state follows the Telegram client color scheme when no stored theme
- applyTelegramLanguage() adopts the Telegram client language when no stored choice,
  called from main.tsx after SDK init (launch params unavailable before init)
This commit is contained in:
c0mrade
2026-05-25 23:31:38 +03:00
parent 8e0b63bac8
commit 0caba3dffb
6 changed files with 96 additions and 4 deletions

View File

@@ -14,6 +14,7 @@ import {
expandViewport,
retrieveLaunchParams,
retrieveRawInitData,
themeParamsState,
} from '@telegram-apps/sdk-react';
const FULLSCREEN_CACHE_KEY = 'cabinet_fullscreen_enabled';
@@ -66,6 +67,46 @@ export function getTelegramInitData(): string | null {
}
}
function isDarkHexColor(hex: string): boolean {
const m = hex.replace('#', '');
const full = m.length === 3 ? m.replace(/(.)/g, '$1$1') : m;
if (full.length !== 6) return false;
const r = parseInt(full.slice(0, 2), 16);
const g = parseInt(full.slice(2, 4), 16);
const b = parseInt(full.slice(4, 6), 16);
// Perceived sRGB luminance; below 0.5 reads as a dark surface.
return (0.299 * r + 0.587 * g + 0.114 * b) / 255 < 0.5;
}
/**
* The Telegram client's effective color scheme ('light' | 'dark'), derived from
* the theme background color. Returns null outside Telegram or before theme params load.
*/
export function getTelegramColorScheme(): 'light' | 'dark' | null {
if (!detectTelegram()) return null;
try {
const bg = themeParamsState()?.bgColor;
return bg ? (isDarkHexColor(bg) ? 'dark' : 'light') : null;
} catch {
return null;
}
}
/**
* The user's Telegram client language as a 2-letter code (e.g. 'en'), or null
* outside Telegram / when unavailable.
*/
export function getTelegramLanguageCode(): string | null {
if (!detectTelegram()) return null;
try {
const user = retrieveLaunchParams().tgWebAppData?.user as { languageCode?: string } | undefined;
const code = user?.languageCode;
return code ? code.split('-')[0].toLowerCase() : null;
} catch {
return null;
}
}
export type TelegramPlatform =
| 'android'
| 'ios'