feat(app-expo): tiered large-text presets with English-friendly default
Replace the boolean large-text flag with three global typography tiers, defaulting new installs to the smallest tier when English is in effect while preserving legacy storage and Chinese defaults. Add a profile sub-screen to pick the tier and unit tests for storage resolution. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { getLocales } from 'expo-localization';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import {
|
||||
@@ -6,7 +7,11 @@ import {
|
||||
setSecureItem,
|
||||
} from '@/core/storage/secure';
|
||||
|
||||
import { supportedLanguages, type AppLanguage } from '@/i18n/resources';
|
||||
import {
|
||||
fallbackLanguage,
|
||||
supportedLanguages,
|
||||
type AppLanguage,
|
||||
} from '@/i18n/resources';
|
||||
|
||||
import { THEME_NAMES, type ThemeName } from '@/constants/theme-bridge';
|
||||
|
||||
@@ -18,6 +23,7 @@ import {
|
||||
|
||||
const KEY_LANGUAGE = 'app_settings_language';
|
||||
const KEY_LARGE_TEXT = 'app_settings_large_text';
|
||||
const KEY_LARGE_TEXT_LEVEL = 'app_settings_large_text_level';
|
||||
const KEY_DARK_MODE = 'app_settings_dark_mode';
|
||||
const KEY_THEME_NAME = 'app_settings_theme_name';
|
||||
const KEY_TTS_SPEAK_DEFAULT = 'app_settings_tts_speak_default';
|
||||
@@ -62,14 +68,73 @@ export async function clearAppLanguage(): Promise<void> {
|
||||
await deleteStored(KEY_LANGUAGE);
|
||||
}
|
||||
|
||||
export async function getLargeText(): Promise<boolean> {
|
||||
const v = await getStored(KEY_LARGE_TEXT);
|
||||
if (v == null || v === '') return true;
|
||||
return v === 'true';
|
||||
export type LargeTextLevel = 0 | 1 | 2;
|
||||
|
||||
function parseStoredLargeTextLevel(raw: string | null): LargeTextLevel | null {
|
||||
if (raw === '0' || raw === '1' || raw === '2') {
|
||||
return Number(raw) as LargeTextLevel;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function setLargeText(value: boolean): Promise<void> {
|
||||
await setStored(KEY_LARGE_TEXT, value ? 'true' : 'false');
|
||||
function deviceLanguageFromLocales(): AppLanguage {
|
||||
const locale = getLocales()[0];
|
||||
const tag = locale?.languageCode ?? locale?.languageTag;
|
||||
if (!tag) return fallbackLanguage;
|
||||
return tag.toLowerCase().startsWith('zh') ? 'zh' : 'en';
|
||||
}
|
||||
|
||||
async function effectiveAppLanguage(): Promise<AppLanguage> {
|
||||
const override = await getAppLanguage();
|
||||
return override ?? deviceLanguageFromLocales();
|
||||
}
|
||||
|
||||
/** 无存储时的默认档位:英文关大字、中文沿用偏大默认 */
|
||||
export function defaultLargeTextLevelForLanguage(lang: AppLanguage): LargeTextLevel {
|
||||
return lang === 'en' ? 0 : 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 纯函数:由原始存储值与当前生效语言解析档位(供单测与实现共用)。
|
||||
*/
|
||||
export function computeLargeTextLevelFromStorage(
|
||||
levelRaw: string | null,
|
||||
legacyLargeTextRaw: string | null,
|
||||
effectiveLanguage: AppLanguage,
|
||||
): LargeTextLevel {
|
||||
const parsed = parseStoredLargeTextLevel(levelRaw);
|
||||
if (parsed !== null) return parsed;
|
||||
if (legacyLargeTextRaw === 'true') return 1;
|
||||
if (legacyLargeTextRaw === 'false') return 0;
|
||||
return defaultLargeTextLevelForLanguage(effectiveLanguage);
|
||||
}
|
||||
|
||||
export async function getLargeTextLevel(): Promise<LargeTextLevel> {
|
||||
const [levelRaw, legacyRaw, lang] = await Promise.all([
|
||||
getStored(KEY_LARGE_TEXT_LEVEL),
|
||||
getStored(KEY_LARGE_TEXT),
|
||||
effectiveAppLanguage(),
|
||||
]);
|
||||
const level = computeLargeTextLevelFromStorage(levelRaw, legacyRaw, lang);
|
||||
|
||||
const hasValidLevel = parseStoredLargeTextLevel(levelRaw) !== null;
|
||||
if (hasValidLevel && legacyRaw != null && legacyRaw !== '') {
|
||||
await deleteStored(KEY_LARGE_TEXT);
|
||||
} else if (
|
||||
!hasValidLevel &&
|
||||
legacyRaw != null &&
|
||||
legacyRaw !== ''
|
||||
) {
|
||||
await setStored(KEY_LARGE_TEXT_LEVEL, String(level));
|
||||
await deleteStored(KEY_LARGE_TEXT);
|
||||
}
|
||||
|
||||
return level;
|
||||
}
|
||||
|
||||
export async function setLargeTextLevel(level: LargeTextLevel): Promise<void> {
|
||||
await setStored(KEY_LARGE_TEXT_LEVEL, String(level));
|
||||
await deleteStored(KEY_LARGE_TEXT);
|
||||
}
|
||||
|
||||
export async function getDarkMode(): Promise<boolean> {
|
||||
|
||||
Reference in New Issue
Block a user