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

82 lines
3.0 KiB
Vue

<template>
<div class="min-h-screen bg-default flex flex-col items-center justify-center px-6 py-12 text-center"
style="padding-top: max(3rem, env(safe-area-inset-top)); padding-bottom: max(3rem, env(safe-area-inset-bottom))">
<!-- Animated Shield Icon -->
<div class="relative mb-8">
<div
class="w-24 h-24 rounded-full bg-primary-950/60 border border-primary-700/40 flex items-center justify-center mx-auto">
<div class="absolute inset-0 rounded-full bg-primary-500/10 animate-ping" />
<UIcon name="i-heroicons-shield-check" class="text-primary-400 text-5xl relative z-10" />
</div>
</div>
<!-- Main Message -->
<div class="max-w-xs mx-auto mb-8">
<h1 class="text-2xl font-bold text-highlighted mb-3 leading-tight">
{{ $t('blocked.title') }}
</h1>
<!-- Blocked domain badge -->
<div v-if="blockedDomain"
class="inline-flex items-center gap-2 bg-red-950/40 border border-red-700/30 rounded-full px-4 py-1.5 mb-4">
<UIcon name="i-heroicons-x-circle" class="text-red-400 text-sm shrink-0" />
<span class="text-red-300 text-sm font-mono truncate max-w-[200px]">{{ blockedDomain }}</span>
</div>
<p class="text-muted text-sm leading-relaxed">
{{ $t('blocked.message') }}
</p>
</div>
<!-- CTA: Open App -->
<div class="w-full max-w-xs space-y-3">
<a href="https://apps.apple.com/app/rebreak" target="_blank" rel="noopener"
class="w-full flex items-center justify-center gap-3 bg-primary-600 hover:bg-primary-500 active:bg-primary-700 text-white font-semibold rounded-2xl px-6 py-4 transition-colors">
<UIcon name="i-heroicons-chat-bubble-left-ellipsis" class="text-xl" />
{{ $t('blocked.talk_lyra') }}
</a>
<button @click="goBack" class="w-full text-dimmed hover:text-muted text-sm py-2 transition-colors">
{{ $t('blocked.back_to_app') }}
</button>
</div>
<!-- Bottom motivational quote -->
<div class="mt-10 max-w-xs">
<p class="text-xs text-dimmed italic leading-relaxed">
{{ quote }}"
</p>
</div>
</div>
</template>
<script setup lang="ts">
definePageMeta({ layout: false });
const { t } = useI18n();
const route = useRoute();
const blockedDomain = computed(() => {
const d = route.query.domain as string | undefined;
return d ? decodeURIComponent(d) : null;
});
const quotes = computed(() => [
t('blocked.quote_1'),
t('blocked.quote_2'),
t('blocked.quote_3'),
t('blocked.quote_4'),
t('blocked.quote_5'),
]);
const quote = computed(() => quotes.value[Math.floor(Math.random() * quotes.value.length)]);
function goBack() {
if (window.history.length > 1) {
window.history.back();
} else {
navigateTo('/');
}
}
</script>