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>
82 lines
3.0 KiB
Vue
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>
|