chahinebrini 5d6c322129 wip: KeyboardAwareSheet migrations + Snake/Tetris UI + iron.png + useMe live-update
Sheets via neuer KeyboardAwareSheet-Composable (in Modal pattern, auto-grow
mit Tastatur, paddingBottom-Lift): EditMail, AddDomain, CreateRoom, ConnectMail.
GameOverScreen behält Spring-Slide-In, nutzt RN Keyboard.addListener für Lift.

- KeyboardAwareSheet.tsx — universal modal with sheet-grow + keyboard-padding
- react-native-keyboard-controller installiert + KeyboardProvider in Root
- Snake: time + ScoreProgressBar + useSnakeSounds (haptic, audio TODO)
- Tetris: title weg, Buttons zentriert, kein Pressable mit style-fn
- DPad-Buttons 60→48, more bg, no scale
- useMe: pub-sub listener pattern für app-weite avatar/nickname-Updates
- dm.tsx: resolveAvatar wrap (iron.png-Warning)
- Mail-error-humanizer + locales

Recovery-Doc-Update in docs/internal/RECOVERY_LOG_2026-05-10.md

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 23:59:25 +02:00

85 lines
2.1 KiB
TypeScript

import { Platform } from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as Application from 'expo-application';
import Constants from 'expo-constants';
const STORAGE_KEY = 'rebreak_device_id';
let cached: string | null = null;
export async function getDeviceId(): Promise<string> {
if (cached) return cached;
if (Platform.OS === 'ios') {
const vendor = await Application.getIosIdForVendorAsync();
if (vendor) {
cached = vendor;
return vendor;
}
}
if (Platform.OS === 'android') {
const androidId = Application.getAndroidId();
if (androidId) {
cached = androidId;
return androidId;
}
}
// Fallback: persisted UUID via AsyncStorage (web / simulator edge cases)
const stored = await AsyncStorage.getItem(STORAGE_KEY).catch(() => null);
if (stored) {
cached = stored;
return stored;
}
const uuid =
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
const r = (Math.random() * 16) | 0;
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
});
await AsyncStorage.setItem(STORAGE_KEY, uuid).catch(() => {});
cached = uuid;
return uuid;
}
export function getPlatformName(): string {
if (Platform.OS === 'ios') return 'ios';
if (Platform.OS === 'android') return 'android';
return 'web';
}
export interface DeviceInfo {
deviceId: string;
platform: string;
name: string;
model: string;
osVersion: string;
appVersion: string;
}
export async function getDeviceInfo(): Promise<DeviceInfo> {
const deviceId = await getDeviceId();
const platform = getPlatformName();
const name =
(Constants as any).deviceName ||
Constants.platform?.ios?.model ||
platform;
const model =
Constants.platform?.ios?.model ||
Constants.platform?.android?.versionCode?.toString() ||
platform;
const osVersion =
Constants.platform?.ios?.systemVersion?.toString() ||
(Platform.Version as string | number)?.toString() ||
'';
const appVersion = Application.nativeApplicationVersion || '';
return { deviceId, platform, name, model, osVersion, appVersion };
}