mirror of
https://github.com/chillpadclub/bedolaga-cabinet.git
synced 2026-07-29 18:13:47 +00:00
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:
@@ -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'
|
||||
|
||||
@@ -2,6 +2,7 @@ import { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import { EnabledThemes, DEFAULT_ENABLED_THEMES } from '../types/theme';
|
||||
import { themeColorsApi } from '../api/themeColors';
|
||||
import { STORAGE_KEYS } from '../config/constants';
|
||||
import { getTelegramColorScheme } from './useTelegramSDK';
|
||||
|
||||
type Theme = 'dark' | 'light';
|
||||
|
||||
@@ -74,6 +75,13 @@ export function useTheme() {
|
||||
if (stored && !enabled[stored]) {
|
||||
return enabled.dark ? 'dark' : 'light';
|
||||
}
|
||||
// No stored preference: follow the Telegram client's color scheme in a Mini App.
|
||||
if (!stored) {
|
||||
const tgScheme = getTelegramColorScheme();
|
||||
if (tgScheme && enabled[tgScheme]) {
|
||||
return tgScheme;
|
||||
}
|
||||
}
|
||||
// Check system preference
|
||||
if (window.matchMedia('(prefers-color-scheme: light)').matches && enabled.light) {
|
||||
return 'light';
|
||||
|
||||
Reference in New Issue
Block a user