From 14452b2a463913d9e37f66262e6102ac09521290 Mon Sep 17 00:00:00 2001 From: chahinebrini Date: Mon, 11 May 2026 15:43:10 +0200 Subject: [PATCH] =?UTF-8?q?refactor(native):=20Pressable=20=E2=86=92=20Tou?= =?UTF-8?q?chableOpacity=20sweep=20(style-fn=20swallows=20Android=20styles?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alle ({...})}> ersetzt — style-Funktion droppt auf Android (New Arch) intermittierend width/height, führt zu 0×0 unsichtbaren Elementen. TouchableOpacity mit activeOpacity ist stabil. Außerdem übrige Pressables (plain style) aus components/ und app/ migriert sowie zwei überschüssige -Tags in chat.tsx + RoomCard.tsx entfernt die TS-Fehler verursacht haben. 64 Dateien, typecheck sauber. Co-Authored-By: Claude Sonnet 4.6 --- apps/rebreak-native/app/(app)/chat.tsx | 24 ++++---- apps/rebreak-native/app/(app)/index.tsx | 18 +++--- .../app/(app)/notifications.tsx | 19 +++---- .../rebreak-native/app/(auth)/confirm-otp.tsx | 19 ++++--- apps/rebreak-native/app/(auth)/confirm.tsx | 7 ++- .../app/(auth)/device-limit.tsx | 14 +++-- .../app/(auth)/forgot-password.tsx | 14 +++-- apps/rebreak-native/app/(auth)/signin.tsx | 33 ++++++----- apps/rebreak-native/app/(auth)/signup.tsx | 38 +++++++------ apps/rebreak-native/app/debug.tsx | 31 ++++------ apps/rebreak-native/app/devices.tsx | 23 ++++---- apps/rebreak-native/app/dm.tsx | 6 +- apps/rebreak-native/app/games.tsx | 45 +++++---------- apps/rebreak-native/app/index.tsx | 9 +-- apps/rebreak-native/app/lyra.tsx | 32 ++++++----- apps/rebreak-native/app/profile/[userId].tsx | 29 +++++----- apps/rebreak-native/app/profile/edit.tsx | 39 ++++++------- apps/rebreak-native/app/room.tsx | 52 +++++++++-------- apps/rebreak-native/app/settings.tsx | 34 ++++++----- apps/rebreak-native/app/urge.tsx | 6 +- .../rebreak-native/components/ComposeCard.tsx | 21 ++++--- .../components/ConfirmAlert.tsx | 21 ++++--- .../components/DeviceLimitReachedSheet.tsx | 8 +-- .../components/KeyboardAwareSheet.tsx | 4 +- .../components/NotificationsDropdown.tsx | 10 ++-- .../components/OptionsBottomSheet.tsx | 51 ++++++++--------- apps/rebreak-native/components/PostCard.tsx | 6 +- .../components/PostCommentsSheet.tsx | 10 ++-- .../components/SuccessAlert.tsx | 16 +++--- .../components/WheelPickerModal.tsx | 10 ++-- .../components/blocker/AddDomainSheet.tsx | 11 ++-- .../components/blocker/CooldownBanner.tsx | 11 ++-- .../blocker/DeactivationExplainerSheet.tsx | 21 ++++--- .../components/blocker/DomainGrid.tsx | 9 ++- .../components/blocker/ProtectionCard.tsx | 10 ++-- .../blocker/ProtectionDetailsSheet.tsx | 21 +++---- .../blocker/ProtectionLockedCard.tsx | 10 ++-- .../components/chat/ChatBubble.tsx | 29 ++++++---- .../components/chat/ChatInput.tsx | 20 ++++--- .../components/chat/CreateRoomSheet.tsx | 20 ++++--- .../components/chat/RoomCard.tsx | 6 +- .../components/devices/AddMacSheet.tsx | 57 ++++++++++--------- .../components/devices/AddWindowsSheet.tsx | 57 ++++++++++--------- .../components/games/GameCard.tsx | 13 ++--- .../components/games/StarRating.tsx | 9 ++- .../components/header/HeaderDropdownMenu.tsx | 16 +++--- .../components/mail/ConnectMailSheet.tsx | 40 ++++++------- .../components/mail/EditMailAccountSheet.tsx | 20 +++---- .../components/mail/MailAccountCard.tsx | 16 ++++-- .../components/mail/MailActivityLog.tsx | 5 +- .../components/mail/MailWeeklyChart.tsx | 5 +- .../profile/ApprovedDomainsList.tsx | 8 +-- .../profile/DemographicsAccordion.tsx | 26 ++++----- .../components/profile/DigaMissionBanner.tsx | 24 ++++---- .../components/profile/ProfileHeader.tsx | 33 +++++------ .../components/profile/StatsBar.tsx | 8 +-- .../components/urge/Breathing.tsx | 6 +- .../components/urge/InlineRatingDrawer.tsx | 24 ++++---- .../components/urge/LlmProviderToggle.tsx | 7 ++- .../components/urge/MessageRow.tsx | 4 +- .../components/urge/ShareSuccessDrawer.tsx | 15 +++-- .../components/urge/SosFeedbackModal.tsx | 24 ++++---- .../components/urge/TtsProviderToggle.tsx | 7 ++- .../components/urge/UrgeGames.tsx | 12 ++-- 64 files changed, 615 insertions(+), 638 deletions(-) diff --git a/apps/rebreak-native/app/(app)/chat.tsx b/apps/rebreak-native/app/(app)/chat.tsx index 8c2209e..a257be1 100644 --- a/apps/rebreak-native/app/(app)/chat.tsx +++ b/apps/rebreak-native/app/(app)/chat.tsx @@ -4,6 +4,7 @@ import { Text, FlatList, Pressable, + TouchableOpacity, ActivityIndicator, Image, RefreshControl, @@ -45,8 +46,7 @@ function DmItem({ conv, onPress }: { conv: DmConversation; onPress: () => void } return ( - {({ pressed }) => ( - + {conv.partnerAvatar ? ( @@ -88,8 +88,7 @@ function DmItem({ conv, onPress }: { conv: DmConversation; onPress: () => void } )} - - )} + ); } @@ -150,19 +149,21 @@ export default function ChatScreen() { {t('chat.title')} {tab === 'groups' && ( - setCreateOpen(true)} - style={({ pressed }) => [styles.createBtn, { opacity: pressed ? 0.7 : 1 }]} + activeOpacity={0.7} + style={styles.createBtn} > - + )} {/* Tabs */} - setTab('groups')} + activeOpacity={0.7} style={[styles.tab, tab === 'groups' && styles.tabActive]} > {t('chat.groups')} - - + setTab('direct')} + activeOpacity={0.7} style={[styles.tab, tab === 'direct' && styles.tabActive]} > {unreadDms} )} - + diff --git a/apps/rebreak-native/app/(app)/index.tsx b/apps/rebreak-native/app/(app)/index.tsx index 9b55ecf..712dd3b 100644 --- a/apps/rebreak-native/app/(app)/index.tsx +++ b/apps/rebreak-native/app/(app)/index.tsx @@ -4,7 +4,7 @@ import { Text, ScrollView, FlatList, - Pressable, + TouchableOpacity, RefreshControl, ActivityIndicator, } from 'react-native'; @@ -110,10 +110,10 @@ export default function HomeScreen() { {/* Filter toggle */} - setFilterOpen((o) => !o)} + activeOpacity={0.6} className="flex-row items-center gap-1.5 self-start" - style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })} > f.value === activeCategory)?.label} )} - + {filterOpen && ( { const active = activeCategory === f.value; return ( - toggleFilter(f.value)} - style={({ pressed }) => ({ - opacity: pressed ? 0.7 : 1, + activeOpacity={0.7} + style={{ flexDirection: 'row', alignItems: 'center', gap: 6, @@ -151,7 +151,7 @@ export default function HomeScreen() { borderWidth: 1, backgroundColor: active ? colors.brandOrange : colors.surface, borderColor: active ? colors.brandOrange : colors.border, - })} + }} > {f.label} - + ); })} diff --git a/apps/rebreak-native/app/(app)/notifications.tsx b/apps/rebreak-native/app/(app)/notifications.tsx index 8ceae8a..4f4018c 100644 --- a/apps/rebreak-native/app/(app)/notifications.tsx +++ b/apps/rebreak-native/app/(app)/notifications.tsx @@ -1,5 +1,5 @@ import { useEffect } from 'react'; -import { View, Text, FlatList, Pressable, RefreshControl } from 'react-native'; +import { View, Text, FlatList, TouchableOpacity, RefreshControl } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; @@ -31,12 +31,13 @@ export default function NotificationsScreen() { return ( - router.back()} + activeOpacity={0.7} style={{ width: 36, height: 36, borderRadius: 18, backgroundColor: colors.surfaceElevated, borderWidth: 1, borderColor: colors.border, alignItems: 'center', justifyContent: 'center' }} > - + @@ -91,11 +92,9 @@ function NotificationRow({ const colors = useColors(); const isUnread = !notif.readAt; return ( - ({ - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} > )} - + - + - + ); } diff --git a/apps/rebreak-native/app/(auth)/confirm-otp.tsx b/apps/rebreak-native/app/(auth)/confirm-otp.tsx index 604a6ad..0b84132 100644 --- a/apps/rebreak-native/app/(auth)/confirm-otp.tsx +++ b/apps/rebreak-native/app/(auth)/confirm-otp.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, KeyboardAvoidingView, Platform, ActivityIndicator, @@ -168,10 +168,11 @@ export default function ConfirmOtpScreen() { )} - {loading ? ( @@ -179,11 +180,12 @@ export default function ConfirmOtpScreen() { ) : ( {t('auth.confirmBtn')} )} - + - 0 || loading} + activeOpacity={0.7} className="py-3 items-center" > @@ -192,14 +194,15 @@ export default function ConfirmOtpScreen() { {resendCooldown > 0 ? t('auth.resendCooldown', { seconds: resendCooldown }) : t('auth.resend')} - + - router.back()} + activeOpacity={0.7} className="py-3 items-center mt-2" > {t('auth.backToSignup')} - + diff --git a/apps/rebreak-native/app/(auth)/confirm.tsx b/apps/rebreak-native/app/(auth)/confirm.tsx index a0075cf..d6b78c0 100644 --- a/apps/rebreak-native/app/(auth)/confirm.tsx +++ b/apps/rebreak-native/app/(auth)/confirm.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { View, Text, Pressable, ActivityIndicator } from 'react-native'; +import { View, Text, TouchableOpacity, ActivityIndicator } from 'react-native'; import { useRouter, useLocalSearchParams } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useTranslation } from 'react-i18next'; @@ -82,12 +82,13 @@ export default function ConfirmScreen() { ) : ( <> {errorMsg} - router.replace('/signin')} + activeOpacity={0.7} className="bg-neutral-100 border border-neutral-200 px-6 py-3 rounded-xl" > {t('auth.backToLoginPlain')} - + )} diff --git a/apps/rebreak-native/app/(auth)/device-limit.tsx b/apps/rebreak-native/app/(auth)/device-limit.tsx index 7c91a5b..b270542 100644 --- a/apps/rebreak-native/app/(auth)/device-limit.tsx +++ b/apps/rebreak-native/app/(auth)/device-limit.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { useRouter } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useTranslation } from 'react-i18next'; @@ -25,19 +25,21 @@ export default function DeviceLimitScreen() { {/* TODO Phase 4: device management — list active devices + revoke button */} - router.replace('/signin')} - className="bg-rebreak-500 px-8 py-4 rounded-xl active:opacity-80" + activeOpacity={0.8} + className="bg-rebreak-500 px-8 py-4 rounded-xl" > {t('auth.toLogin')} - + - router.push('/signin')} + activeOpacity={0.7} className="py-3 mt-2" > {t('auth.deviceLimitUpgrade')} - + ); diff --git a/apps/rebreak-native/app/(auth)/forgot-password.tsx b/apps/rebreak-native/app/(auth)/forgot-password.tsx index 866be24..da69940 100644 --- a/apps/rebreak-native/app/(auth)/forgot-password.tsx +++ b/apps/rebreak-native/app/(auth)/forgot-password.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, KeyboardAvoidingView, Platform, ActivityIndicator, @@ -78,10 +78,11 @@ export default function ForgotPasswordScreen() { )} - {loading ? ( @@ -89,7 +90,7 @@ export default function ForgotPasswordScreen() { ) : ( {t('auth.resetPasswordSend')} )} - + ) : ( @@ -100,12 +101,13 @@ export default function ForgotPasswordScreen() { )} - router.back()} + activeOpacity={0.7} className="py-4 items-center mt-2" > {t('auth.backToLogin')} - + diff --git a/apps/rebreak-native/app/(auth)/signin.tsx b/apps/rebreak-native/app/(auth)/signin.tsx index 5534de6..23b5eea 100644 --- a/apps/rebreak-native/app/(auth)/signin.tsx +++ b/apps/rebreak-native/app/(auth)/signin.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView, @@ -100,10 +100,11 @@ export default function SignInScreen() { {/* OAuth Buttons */} - onOAuth('google')} disabled={isLoading} - className="flex-row items-center justify-center gap-3 bg-white border border-neutral-200 rounded-xl mb-3 active:opacity-80 disabled:opacity-40" + activeOpacity={0.8} + className="flex-row items-center justify-center gap-3 bg-white border border-neutral-200 rounded-xl mb-3 disabled:opacity-40" style={{ paddingVertical: 14 }} > {oauthLoading === 'google' ? ( @@ -112,12 +113,13 @@ export default function SignInScreen() { )} {t('auth.googleSignin')} - + - onOAuth('apple')} disabled={isLoading} - className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 active:opacity-80 disabled:opacity-40" + activeOpacity={0.8} + className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40" style={{ paddingVertical: 14 }} > {oauthLoading === 'apple' ? ( @@ -126,7 +128,7 @@ export default function SignInScreen() { )} {t('auth.appleSignin')} - + {/* Divider */} @@ -159,21 +161,23 @@ export default function SignInScreen() { onChangeText={setPassword} /> - router.push('/forgot-password')} + activeOpacity={0.7} className="self-end py-2 mb-4" > {t('auth.forgotPassword')} - + {error && ( {error} )} - {submitting ? ( @@ -181,17 +185,18 @@ export default function SignInScreen() { ) : ( {t('auth.signin')} )} - + - router.push('/signup')} + activeOpacity={0.7} className="py-4 items-center mt-2" > {t('auth.noAccount')}{' '} {t('auth.signup')} - + diff --git a/apps/rebreak-native/app/(auth)/signup.tsx b/apps/rebreak-native/app/(auth)/signup.tsx index 4b80b64..111bd42 100644 --- a/apps/rebreak-native/app/(auth)/signup.tsx +++ b/apps/rebreak-native/app/(auth)/signup.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, KeyboardAvoidingView, Platform, ScrollView, @@ -124,10 +124,11 @@ export default function SignUpScreen() { {/* OAuth Buttons */} - onOAuth('google')} disabled={isLoading} - className="flex-row items-center justify-center gap-3 bg-white border border-neutral-200 rounded-xl mb-3 active:opacity-80 disabled:opacity-40" + activeOpacity={0.8} + className="flex-row items-center justify-center gap-3 bg-white border border-neutral-200 rounded-xl mb-3 disabled:opacity-40" style={{ paddingVertical: 14 }} > {oauthLoading === 'google' ? ( @@ -136,12 +137,13 @@ export default function SignUpScreen() { )} {t('auth.googleSignup')} - + - onOAuth('apple')} disabled={isLoading} - className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 active:opacity-80 disabled:opacity-40" + activeOpacity={0.8} + className="flex-row items-center justify-center gap-3 bg-neutral-900 rounded-xl mb-6 disabled:opacity-40" style={{ paddingVertical: 14 }} > {oauthLoading === 'apple' ? ( @@ -150,7 +152,7 @@ export default function SignUpScreen() { )} {t('auth.appleSignup')} - + {/* Divider */} @@ -226,10 +228,11 @@ export default function SignUpScreen() { {HERO_AVATARS.map((avatar) => { const selected = avatar.id === avatarId; return ( - setAvatarId(avatar.id)} disabled={isLoading} + activeOpacity={0.7} className={`rounded-full ${selected ? 'opacity-100' : 'opacity-40'}`} > - + ); })} @@ -251,9 +254,10 @@ export default function SignUpScreen() { {/* Terms Checkbox */} - setTermsAccepted(!termsAccepted)} disabled={isLoading} + activeOpacity={0.7} className="flex-row items-start gap-3 mb-6" > {t('auth.termsLink')} {t('auth.acceptTermsSuffix')} - + - {submitting ? ( @@ -283,17 +288,18 @@ export default function SignUpScreen() { ) : ( {t('auth.signupTitle')} )} - + - router.push('/signin')} + activeOpacity={0.7} className="py-4 items-center mt-2" > {t('auth.alreadyRegistered')}{' '} {t('auth.signin')} - + diff --git a/apps/rebreak-native/app/debug.tsx b/apps/rebreak-native/app/debug.tsx index fb3a2fc..1c6b68e 100644 --- a/apps/rebreak-native/app/debug.tsx +++ b/apps/rebreak-native/app/debug.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { View, Text, ScrollView, Pressable, Alert } from 'react-native'; +import { View, Text, ScrollView, TouchableOpacity, Alert } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; @@ -36,22 +36,14 @@ export default function DebugScreen() { borderBottomColor: 'rgba(0,0,0,0.06)', }} > - router.back()} hitSlop={8} - style={({ pressed }) => ({ - opacity: pressed ? 0.6 : 1, - })} + activeOpacity={0.6} + style={{ width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }} > - - - - + + Debug @@ -214,18 +206,19 @@ function PlanOverrideToggle({ const isActive = plan === currentPlan; const accent = PLAN_COLOR[plan]; return ( - switchPlan(plan)} disabled={loading || isActive} - style={({ pressed }) => ({ + activeOpacity={0.7} + style={{ flex: 1, paddingVertical: 10, borderRadius: 10, alignItems: 'center', backgroundColor: isActive ? accent : colors.surfaceElevated, - opacity: loading ? 0.5 : pressed ? 0.7 : 1, - })} + opacity: loading ? 0.5 : 1, + }} > {plan} - + ); })} diff --git a/apps/rebreak-native/app/devices.tsx b/apps/rebreak-native/app/devices.tsx index 0a26711..9e8a7ef 100644 --- a/apps/rebreak-native/app/devices.tsx +++ b/apps/rebreak-native/app/devices.tsx @@ -2,7 +2,6 @@ import { ActivityIndicator, Alert, Platform, - Pressable, ScrollView, Text, TouchableOpacity, @@ -222,13 +221,13 @@ function MobileDeviceRow({ {!device.isCurrent ? ( - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + ) : null} ); @@ -324,12 +323,12 @@ function ProtectedDeviceRow({ onPressAction={({ nativeEvent: { event } }) => handleMenuSelect(event)} shouldOpenOnLongPress={false} > - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + ); @@ -579,19 +578,19 @@ export default function DevicesScreen() { {t('devices.subtitle_legend')} - ({ + {t('devices.upgrade_cta')} - + )} diff --git a/apps/rebreak-native/app/dm.tsx b/apps/rebreak-native/app/dm.tsx index 3fee143..24da0ff 100644 --- a/apps/rebreak-native/app/dm.tsx +++ b/apps/rebreak-native/app/dm.tsx @@ -3,7 +3,7 @@ import { View, Text, FlatList, - Pressable, + TouchableOpacity, KeyboardAvoidingView, Platform, ActivityIndicator, @@ -236,9 +236,9 @@ export default function DmScreen() { {/* Header */} - router.back()} hitSlop={8}> + router.back()} hitSlop={8} activeOpacity={0.7}> - + {partner?.avatar ? ( diff --git a/apps/rebreak-native/app/games.tsx b/apps/rebreak-native/app/games.tsx index c9a6d36..f8b00ac 100644 --- a/apps/rebreak-native/app/games.tsx +++ b/apps/rebreak-native/app/games.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import { View, Text, Pressable, ScrollView } from 'react-native'; +import { View, Text, TouchableOpacity, ScrollView } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; @@ -83,26 +83,17 @@ export default function GamesScreen() { borderBottomColor: colors.border, }} > - exit()} hitSlop={10} - style={({ pressed }) => ({ - opacity: pressed ? 0.6 : 1, - })} + activeOpacity={0.6} + style={{ flexDirection: 'row', alignItems: 'center', gap: 4, paddingHorizontal: 6, paddingVertical: 6 }} > - - - - {t('games.back_to_picker')} - - - + + + {t('games.back_to_picker')} + + {/* Title bewusst entfernt — der Game-Picker hat das Spiel schon ausgewählt, Wiederholung im Header lenkt nur ab. Spacer balanciert den Back-Button. */} @@ -141,22 +132,14 @@ export default function GamesScreen() { borderBottomColor: colors.border, }} > - router.back()} hitSlop={8} - style={({ pressed }) => ({ - opacity: pressed ? 0.6 : 1, - })} + activeOpacity={0.6} + style={{ width: 40, height: 40, alignItems: 'center', justifyContent: 'center' }} > - - - - + + {t('games.title')} diff --git a/apps/rebreak-native/app/index.tsx b/apps/rebreak-native/app/index.tsx index eb3402e..dbd7fbf 100644 --- a/apps/rebreak-native/app/index.tsx +++ b/apps/rebreak-native/app/index.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { useRouter } from 'expo-router'; import { SafeAreaView } from 'react-native-safe-area-context'; import { useTranslation } from 'react-i18next'; @@ -15,12 +15,13 @@ export default function HomeScreen() { {t('landing.tagline')} - router.push('/signin')} - className="bg-rebreak-500 px-8 py-4 rounded-full active:opacity-80" + activeOpacity={0.8} + className="bg-rebreak-500 px-8 py-4 rounded-full" > {t('landing.start')} - + {t('landing.version')} diff --git a/apps/rebreak-native/app/lyra.tsx b/apps/rebreak-native/app/lyra.tsx index 24b8e18..a7532e7 100644 --- a/apps/rebreak-native/app/lyra.tsx +++ b/apps/rebreak-native/app/lyra.tsx @@ -9,7 +9,7 @@ import { Text, TextInput, FlatList, - Pressable, + TouchableOpacity, Platform, Animated, Keyboard, @@ -550,9 +550,9 @@ export default function CoachScreen() { {/* Floating header — no bar, avatar + 2 icon buttons hover over chat */} - router.replace('/(app)' as never)} hitSlop={12}> + router.replace('/(app)' as never)} hitSlop={12} activeOpacity={0.7}> - + @@ -587,17 +587,17 @@ export default function CoachScreen() { {t('coach.speaking')} - + - + )} - + - + {/* Content area */} @@ -637,9 +637,9 @@ export default function CoachScreen() { {/* Scroll-to-bottom button */} {showScrollBtn && ( - + - + )} )} @@ -648,9 +648,9 @@ export default function CoachScreen() { 0 ? 8 : Math.max(12, insets.bottom), backgroundColor: colors.bg, borderTopColor: colors.border }]}> {isRecording ? ( - + - + {formatDuration(recordingDuration)} @@ -677,28 +677,30 @@ export default function CoachScreen() { )} {!isTranscribing && ( - - + )} {!isRecording && !isTranscribing && input.trim() !== '' && ( - - + )} diff --git a/apps/rebreak-native/app/profile/[userId].tsx b/apps/rebreak-native/app/profile/[userId].tsx index 6c39bde..83d8563 100644 --- a/apps/rebreak-native/app/profile/[userId].tsx +++ b/apps/rebreak-native/app/profile/[userId].tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { View, Text, ScrollView, Pressable, Image } from 'react-native'; +import { View, Text, ScrollView, TouchableOpacity, Image } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useLocalSearchParams, useRouter } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; @@ -119,13 +119,14 @@ export default function ForeignProfileScreen() { paddingHorizontal: 12, }} > - router.back()} hitSlop={8} - style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1, padding: 8 })} + activeOpacity={0.5} + style={{ padding: 8 }} > - + Profil @@ -204,15 +205,13 @@ export default function ForeignProfileScreen() { - { // TODO: POST /api/social/follow/[userId] resp. DELETE bei unfollow setIsFollowing((v) => !v); }} - style={({ pressed }) => ({ - flex: 1, - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ flex: 1 }} > - - + { // TODO: navigate to DM with this userId router.push(`/dm`); }} - style={({ pressed }) => ({ - flex: 1, - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ flex: 1 }} > - + diff --git a/apps/rebreak-native/app/profile/edit.tsx b/apps/rebreak-native/app/profile/edit.tsx index 08caca6..e2020d0 100644 --- a/apps/rebreak-native/app/profile/edit.tsx +++ b/apps/rebreak-native/app/profile/edit.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, ScrollView, Image, ActivityIndicator, @@ -141,22 +141,22 @@ export default function ProfileEditScreen() { backgroundColor: colors.bg, }} > - router.back()} hitSlop={10} - style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1, marginRight: 12 })} + activeOpacity={0.5} + style={{ marginRight: 12 }} > - + {t('profile.edit_title')} - ({ - opacity: pressed || saving || !hasChanges || !nickname.trim() ? 0.4 : 1, - })} + activeOpacity={0.4} + style={{ opacity: saving || !hasChanges || !nickname.trim() ? 0.4 : 1 }} > {saving ? ( @@ -171,7 +171,7 @@ export default function ProfileEditScreen() { {t('profile.edit_save')} )} - + - ({ - marginTop: 12, - flexDirection: 'row', - alignItems: 'center', - gap: 6, - opacity: pressed ? 0.5 : 1, - })} + activeOpacity={0.5} + style={{ marginTop: 12, flexDirection: 'row', alignItems: 'center', gap: 6 }} > {t('profile.edit_photo_cta')} - + {/* Preset avatars */} @@ -249,15 +244,13 @@ export default function ProfileEditScreen() { {HERO_AVATARS.map((avatar) => { const isSelected = !photoUri && avatarId === avatar.id; return ( - { setAvatarId(avatar.id); setPhotoUri(null); }} - style={({ pressed }) => ({ - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} > - + ); })} diff --git a/apps/rebreak-native/app/room.tsx b/apps/rebreak-native/app/room.tsx index a092e31..2c0acbe 100644 --- a/apps/rebreak-native/app/room.tsx +++ b/apps/rebreak-native/app/room.tsx @@ -4,6 +4,7 @@ import { Text, FlatList, Pressable, + TouchableOpacity, Image, Modal, TextInput, @@ -299,9 +300,9 @@ export default function RoomScreen() { {/* Header */} - router.back()} hitSlop={8}> + router.back()} hitSlop={8} activeOpacity={0.7}> - + {room?.avatarUrl ? ( @@ -321,9 +322,9 @@ export default function RoomScreen() { )} - setSettingsOpen(true)} hitSlop={8}> + setSettingsOpen(true)} hitSlop={8} activeOpacity={0.7}> - + {isLoading || !room ? ( @@ -342,20 +343,18 @@ export default function RoomScreen() { {t('chat.join_pending')} ) : ( - [ - styles.joinBtn, - { opacity: pressed || joining ? 0.7 : 1 }, - ]} + activeOpacity={0.7} + style={[styles.joinBtn, joining && { opacity: 0.7 }]} > {joining ? ( ) : ( {t('chat.join')} )} - + )} ) : ( @@ -519,9 +518,9 @@ function RoomSettingsModal({ - + - + {t('chat.settings')} @@ -529,8 +528,9 @@ function RoomSettingsModal({ {/* Avatar + Name */} - {room.avatarUrl ? ( @@ -545,7 +545,7 @@ function RoomSettingsModal({ )} - + {room.name} {room.description && {room.description}} @@ -564,22 +564,24 @@ function RoomSettingsModal({ {req.nickname ?? 'Anonym'} - handleRequest(req.userId, 'approve')} + activeOpacity={0.7} > {t('chat.approve')} - - + handleRequest(req.userId, 'reject')} + activeOpacity={0.7} > {t('chat.reject')} - + )) )} @@ -610,18 +612,20 @@ function RoomSettingsModal({ {isAdmin && m.role === 'member' && ( <> - handlePromote(m.userId)} + activeOpacity={0.7} > Admin - - + handleBan(m.userId)} + activeOpacity={0.7} > Ban - + )} @@ -630,10 +634,10 @@ function RoomSettingsModal({ {/* Leave */} {!room.isDefault && ( - + {t('chat.leave_room')} - + )} diff --git a/apps/rebreak-native/app/settings.tsx b/apps/rebreak-native/app/settings.tsx index 8d84f60..7dd2bf7 100644 --- a/apps/rebreak-native/app/settings.tsx +++ b/apps/rebreak-native/app/settings.tsx @@ -2,9 +2,9 @@ import { Alert, Linking, Platform, - Pressable, ScrollView, Text, + TouchableOpacity, View, } from 'react-native'; import { useRef, useState } from 'react'; @@ -100,7 +100,7 @@ function SubscriptionSheet({ plan, colors, t }: SubscriptionSheetProps) { {t('settings.subscription_sheet_body')} - { // TODO: für iOS-Submission ggf. zu nicht-tippbarem Text degradieren // (Apple Guideline 3.1.1: externe Abo-Links können Review-Ablehnung triggern, @@ -108,13 +108,13 @@ function SubscriptionSheet({ plan, colors, t }: SubscriptionSheetProps) { // sollte ok sein, ist aber ungeprüft — bei Submission erneut prüfen.) Linking.openURL('https://rebreak.org/account'); }} - style={({ pressed }) => ({ + activeOpacity={0.8} + style={{ backgroundColor: accentColor, borderRadius: 14, paddingVertical: 14, alignItems: 'center', - opacity: pressed ? 0.8 : 1, - })} + }} > {t('settings.subscription_sheet_cta')} - + ); } @@ -472,9 +472,9 @@ export default function SettingsScreen() { } shouldOpenOnLongPress={false} > - ({ opacity: pressed ? 0.6 : 1 })} + activeOpacity={0.6} > - + ); } - // Standard-Row: ganze Pressable als Tap-Target return ( - ({ - opacity: row.soon ? 0.5 : pressed ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ opacity: row.soon ? 0.5 : 1 }} > {rowLeft} @@ -555,7 +553,7 @@ export default function SettingsScreen() { /> )} - + ); })} @@ -630,13 +628,13 @@ export default function SettingsScreen() { {voiceOptions.map((opt, idx) => { const isSelected = opt.value === selectedVoice; return ( - { setSelectedVoice(opt.value); voiceSheetRef.current?.dismiss(); }} - style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1 })} + activeOpacity={0.6} > ) : null} - + ); })} diff --git a/apps/rebreak-native/app/urge.tsx b/apps/rebreak-native/app/urge.tsx index f8910de..e7ece2f 100644 --- a/apps/rebreak-native/app/urge.tsx +++ b/apps/rebreak-native/app/urge.tsx @@ -1227,11 +1227,7 @@ export default function SOSScreen() { key={chip.action} onPress={() => handleChip(chip.action)} disabled={thinking} - style={({ pressed }) => [ - st.chip, - pressed && st.chipPressed, - thinking && { opacity: 0.4 }, - ]} + style={[st.chip, thinking && { opacity: 0.4 }]} > {iconName && } diff --git a/apps/rebreak-native/components/ComposeCard.tsx b/apps/rebreak-native/components/ComposeCard.tsx index afa1035..24f462e 100644 --- a/apps/rebreak-native/components/ComposeCard.tsx +++ b/apps/rebreak-native/components/ComposeCard.tsx @@ -4,6 +4,7 @@ import { Text, TextInput, Pressable, + TouchableOpacity, Image, ActivityIndicator, Alert, @@ -133,7 +134,6 @@ export function ComposeCard({ onPosted }: Props) { hitSlop={{ top: 9, bottom: 9, left: 9, right: 9 }} android_ripple={{ color: 'rgba(255,255,255,0.18)', borderless: true, radius: 22 }} className="absolute top-2 right-2 w-7 h-7 rounded-full bg-black/50 items-center justify-center" - style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })} > @@ -148,34 +148,33 @@ export function ComposeCard({ onPosted }: Props) { onPress={pickImage} android_ripple={{ color: 'rgba(0,0,0,0.08)', borderless: true, radius: 22 }} className="flex-row items-center gap-1.5 px-2" - style={({ pressed }) => ({ opacity: pressed ? 0.6 : 1, height: 44 })} + style={{ height: 44 }} > {t('community.image')} - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > {t('common.cancel')} - - { if (!content.trim() || posting) return; submit(); }} + + { if (!content.trim() || posting) return; submit(); }} disabled={!content.trim() || posting} + activeOpacity={0.5} className="bg-rebreak-500 items-center justify-center rounded-full px-5 h-8" - style={({ pressed }) => ({ - opacity: pressed || !content.trim() || posting ? 0.5 : 1, - })} + style={{ opacity: !content.trim() || posting ? 0.5 : 1 }} > {posting ? ( ) : ( {t('community.share')} )} - + )} diff --git a/apps/rebreak-native/components/ConfirmAlert.tsx b/apps/rebreak-native/components/ConfirmAlert.tsx index 9d502dd..fe783bf 100644 --- a/apps/rebreak-native/components/ConfirmAlert.tsx +++ b/apps/rebreak-native/components/ConfirmAlert.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { Modal, View, Text, Pressable, Animated, Easing } from 'react-native'; +import { Modal, View, Text, TouchableOpacity, Animated, Easing } from 'react-native'; // Wichtig (UX-Entscheidung 2026-05-05): Icon im Confirm-Modal NICHT animieren — // User sieht zwei Modals nacheinander (Confirm → Success), beide animierte Icons // = visuelle Doppel-Eskalation, wirkt verwirrend. Daher: Card animiert auf, @@ -74,7 +74,8 @@ export function ConfirmAlert({ return ( - - {}} style={{ width: '85%', maxWidth: 340 }}> + {}} style={{ width: '85%', maxWidth: 340 }}> - {resolvedCancelLabel} - + - {resolvedConfirmLabel} - + - - + + ); } diff --git a/apps/rebreak-native/components/DeviceLimitReachedSheet.tsx b/apps/rebreak-native/components/DeviceLimitReachedSheet.tsx index 29f8375..08dba52 100644 --- a/apps/rebreak-native/components/DeviceLimitReachedSheet.tsx +++ b/apps/rebreak-native/components/DeviceLimitReachedSheet.tsx @@ -1,4 +1,4 @@ -import { ActivityIndicator, Platform, Pressable, Text, View } from 'react-native'; +import { ActivityIndicator, Platform, TouchableOpacity, Text, View } from 'react-native'; import { useEffect, useRef, useState } from 'react'; import { TrueSheet, type SheetDetent } from '@lodev09/react-native-true-sheet'; import { Ionicons } from '@expo/vector-icons'; @@ -106,13 +106,13 @@ function DeviceLimitRow({ {removing ? ( ) : ( - onRemove(device.id)} hitSlop={8} - style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + )} ); diff --git a/apps/rebreak-native/components/KeyboardAwareSheet.tsx b/apps/rebreak-native/components/KeyboardAwareSheet.tsx index 85d33e1..c89d0a8 100644 --- a/apps/rebreak-native/components/KeyboardAwareSheet.tsx +++ b/apps/rebreak-native/components/KeyboardAwareSheet.tsx @@ -5,7 +5,7 @@ import { Keyboard, Modal, Platform, - Pressable, + TouchableOpacity, StyleProp, View, ViewStyle, @@ -162,7 +162,7 @@ export function KeyboardAwareSheet({ opacity: backdropOpacity, }} > - {dismissOnBackdrop && } + {dismissOnBackdrop && } {/* Outer: animated height (JS-driver) */} diff --git a/apps/rebreak-native/components/NotificationsDropdown.tsx b/apps/rebreak-native/components/NotificationsDropdown.tsx index 529beb9..39da854 100644 --- a/apps/rebreak-native/components/NotificationsDropdown.tsx +++ b/apps/rebreak-native/components/NotificationsDropdown.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { View, Text, Pressable, Modal, FlatList, Animated, Image } from 'react-native'; +import { View, Text, Pressable, TouchableOpacity, Modal, FlatList, Animated, Image } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useRouter, type RelativePathString } from 'expo-router'; import { useTranslation } from 'react-i18next'; @@ -239,11 +239,9 @@ function NotificationRow({ const avatarUrl = isSocial ? resolveAvatar(notif.actorAvatar, notif.actorName) : null; return ( - ({ - opacity: pressed ? 0.65 : 1, - })} + activeOpacity={0.65} > - + ); } diff --git a/apps/rebreak-native/components/OptionsBottomSheet.tsx b/apps/rebreak-native/components/OptionsBottomSheet.tsx index 7586724..2e08b16 100644 --- a/apps/rebreak-native/components/OptionsBottomSheet.tsx +++ b/apps/rebreak-native/components/OptionsBottomSheet.tsx @@ -18,6 +18,7 @@ import { View, Text, Pressable, + TouchableOpacity, Animated, Easing, } from 'react-native'; @@ -177,15 +178,13 @@ export function OptionsBottomSheet({ const isSelected = value !== null && value !== undefined && opt.value === value; return ( - { onSelect(opt.value); close(); }} - style={({ pressed }) => ({ - backgroundColor: pressed ? 'rgba(0,0,0,0.06)' : 'transparent', - })} + activeOpacity={0.7} > ({ {opt.label} - + ); })} {/* Cancel-Card — separat, bold */} - - {({ pressed }) => ( - + + - - Abbrechen - - - )} - + Abbrechen + + + ); diff --git a/apps/rebreak-native/components/PostCard.tsx b/apps/rebreak-native/components/PostCard.tsx index e69e1a5..8b6e6ce 100644 --- a/apps/rebreak-native/components/PostCard.tsx +++ b/apps/rebreak-native/components/PostCard.tsx @@ -275,7 +275,6 @@ function PostCardImpl({ post, onCommentPress }: Props) { hitSlop={{ top: 12, bottom: 12, left: 12, right: 12 }} android_ripple={{ color: 'rgba(220,38,38,0.12)', borderless: true, radius: 22 }} className="flex-row items-center gap-1.5" - style={({ pressed }) => ({ opacity: pressed ? 0.5 : 1, transform: [{ scale: pressed ? 0.94 : 1 }] })} > ({ opacity: pressed ? 0.5 : 1, transform: [{ scale: pressed ? 0.94 : 1 }] })} > {post.commentsCount > 0 && ( @@ -514,7 +512,7 @@ function DomainVoteCard({ onPress={() => onVote('yes')} disabled={voting} className="flex-1 flex-row items-center justify-center gap-1.5 h-9 rounded-xl border border-rebreak-500" - style={({ pressed }) => ({ opacity: pressed || voting ? 0.5 : 1 })} + style={{ opacity: voting ? 0.5 : 1 }} > @@ -525,7 +523,7 @@ function DomainVoteCard({ onPress={() => onVote('no')} disabled={voting} className="flex-1 flex-row items-center justify-center gap-1.5 h-9 rounded-xl border border-neutral-300" - style={({ pressed }) => ({ opacity: pressed || voting ? 0.5 : 1 })} + style={{ opacity: voting ? 0.5 : 1 }} > diff --git a/apps/rebreak-native/components/PostCommentsSheet.tsx b/apps/rebreak-native/components/PostCommentsSheet.tsx index 88bb00d..5faf3bd 100644 --- a/apps/rebreak-native/components/PostCommentsSheet.tsx +++ b/apps/rebreak-native/components/PostCommentsSheet.tsx @@ -6,6 +6,7 @@ import { FlatList, TextInput, Pressable, + TouchableOpacity, Keyboard, Platform, ActivityIndicator, @@ -393,12 +394,11 @@ export function PostCommentsSheet({ postId, visible, onClose }: Props) { onSubmitEditing={submit} blurOnSubmit={false} /> - ({ - opacity: pressed || !text.trim() || submitting ? 0.5 : 1, - })} + activeOpacity={0.5} + style={{ opacity: !text.trim() || submitting ? 0.5 : 1 }} > )} - + diff --git a/apps/rebreak-native/components/SuccessAlert.tsx b/apps/rebreak-native/components/SuccessAlert.tsx index 5185b7e..0cecf1b 100644 --- a/apps/rebreak-native/components/SuccessAlert.tsx +++ b/apps/rebreak-native/components/SuccessAlert.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from 'react'; -import { Modal, View, Text, Pressable, Animated, Easing } from 'react-native'; +import { Modal, View, Text, TouchableOpacity, Animated, Easing } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; @@ -73,7 +73,8 @@ export function SuccessAlert({ visible, title, message, onClose }: Props) { return ( {/* Backdrop — Pressable damit Tap-outside schließt */} - {/* Card — Pressable mit onPress={()=>{}} damit Tap auf Card NICHT bubbelt * zum Backdrop und das Modal schließt. */} - {}} style={{ width: '85%', maxWidth: 320 }}> + {}} style={{ width: '85%', maxWidth: 320 }}> )} - {t('common.ok')} - + - - + + ); } diff --git a/apps/rebreak-native/components/WheelPickerModal.tsx b/apps/rebreak-native/components/WheelPickerModal.tsx index 8b2ee81..4f9b05e 100644 --- a/apps/rebreak-native/components/WheelPickerModal.tsx +++ b/apps/rebreak-native/components/WheelPickerModal.tsx @@ -12,7 +12,7 @@ * Für kurze Listen (3-7 items) bleibt ActionSheet besser (siehe useNativeActionSheet). */ import { useEffect, useState } from 'react'; -import { Modal, View, Text, Pressable } from 'react-native'; +import { Modal, View, Text, Pressable, TouchableOpacity } from 'react-native'; import { Picker } from '@react-native-picker/picker'; import { useColors } from '../lib/theme'; @@ -91,7 +91,7 @@ export function WheelPickerModal({ borderBottomColor: colors.border, }} > - + ({ > Abbrechen - + ({ > {title} - + ({ > Fertig - + {/* Wheel — native iOS UIPickerView */} diff --git a/apps/rebreak-native/components/blocker/AddDomainSheet.tsx b/apps/rebreak-native/components/blocker/AddDomainSheet.tsx index 25eb27c..ca4d351 100644 --- a/apps/rebreak-native/components/blocker/AddDomainSheet.tsx +++ b/apps/rebreak-native/components/blocker/AddDomainSheet.tsx @@ -4,6 +4,7 @@ import { Text, TextInput, Pressable, + TouchableOpacity, Image, ActivityIndicator, } from 'react-native'; @@ -262,13 +263,11 @@ export function AddDomainSheet({ visible, tier, onClose, onAdd }: Props) { {/* Add-Button */} - ({ - opacity: pressed ? 0.85 : 1, - marginBottom: insets.bottom > 0 ? 8 : 12, - })} + activeOpacity={0.85} + style={{ marginBottom: insets.bottom > 0 ? 8 : 12 }} > )} - + ); diff --git a/apps/rebreak-native/components/blocker/CooldownBanner.tsx b/apps/rebreak-native/components/blocker/CooldownBanner.tsx index 4c294d1..a86c689 100644 --- a/apps/rebreak-native/components/blocker/CooldownBanner.tsx +++ b/apps/rebreak-native/components/blocker/CooldownBanner.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable, ActivityIndicator } from 'react-native'; +import { View, Text, TouchableOpacity, ActivityIndicator } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -52,13 +52,12 @@ export function CooldownBanner({ remainingFormatted, onCancel }: Props) { {remainingFormatted} - ({ - opacity: pressed || cancelling ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ opacity: cancelling ? 0.7 : 1 }} > )} - + ); } diff --git a/apps/rebreak-native/components/blocker/DeactivationExplainerSheet.tsx b/apps/rebreak-native/components/blocker/DeactivationExplainerSheet.tsx index df8c4ab..8b462c2 100644 --- a/apps/rebreak-native/components/blocker/DeactivationExplainerSheet.tsx +++ b/apps/rebreak-native/components/blocker/DeactivationExplainerSheet.tsx @@ -1,4 +1,4 @@ -import { Modal, View, Text, Pressable, ScrollView, ActionSheetIOS, Platform, Alert } from 'react-native'; +import { Modal, View, Text, Pressable, TouchableOpacity, ScrollView, ActionSheetIOS, Platform, Alert } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -138,11 +138,9 @@ export function DeactivationExplainerSheet({ {/* Primary Deflector */} - ({ - opacity: pressed ? 0.85 : 1, - })} + activeOpacity={0.85} > - + {/* Destructive secondary */} - ({ - opacity: pressed || submitting ? 0.5 : 1, + activeOpacity={0.5} + style={{ + opacity: submitting ? 0.5 : 1, alignSelf: 'center', paddingVertical: 12, - })} + }} > {submitting ? t('blocker.deactivation_starting') : t('blocker.deactivation_start_anyway')} - + diff --git a/apps/rebreak-native/components/blocker/DomainGrid.tsx b/apps/rebreak-native/components/blocker/DomainGrid.tsx index 7e50b80..a6eb570 100644 --- a/apps/rebreak-native/components/blocker/DomainGrid.tsx +++ b/apps/rebreak-native/components/blocker/DomainGrid.tsx @@ -3,6 +3,7 @@ import { View, Text, Pressable, + TouchableOpacity, Image, ActivityIndicator, } from 'react-native'; @@ -138,11 +139,9 @@ export function DomainGrid({ domains, tier, onAdd, onSubmit, onUpgradePro }: Pro {/* Limit-Reached Upsell (nur Free) */} {tier.atLimit && tier.plan === 'free' && ( - ({ - opacity: pressed ? 0.85 : 1, - })} + activeOpacity={0.85} > - + )} {/* Empty State */} diff --git a/apps/rebreak-native/components/blocker/ProtectionCard.tsx b/apps/rebreak-native/components/blocker/ProtectionCard.tsx index d4a4088..de4f65f 100644 --- a/apps/rebreak-native/components/blocker/ProtectionCard.tsx +++ b/apps/rebreak-native/components/blocker/ProtectionCard.tsx @@ -1,4 +1,4 @@ -import { View, Text, Switch, Pressable, ActivityIndicator } from 'react-native'; +import { View, Text, Switch, TouchableOpacity, ActivityIndicator } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import type { ProtectionState } from '../../lib/protection'; @@ -89,12 +89,10 @@ export function ProtectionCard({ state, loading, onActivate, onPressSettings }: {loading ? ( ) : isActive ? ( - ({ - opacity: pressed ? 0.6 : 1, - })} + activeOpacity={0.6} accessibilityLabel={t('blocker.protection_settings_a11y')} > - + ) : ( - {/* MEHR INFO – outline button: Pressable=card, inner View=flex-row */} - ({ - marginTop: 4, - opacity: pressed ? 0.75 : 1, - })} + activeOpacity={0.75} + style={{ marginTop: 4 }} > - + @@ -676,11 +675,9 @@ function FaqItem({ question, answer }: { question: string; answer: string }) { backgroundColor: colors.bg, }} > - setOpen((v) => !v)} - style={({ pressed }) => ({ - opacity: pressed ? 0.75 : 1, - })} + activeOpacity={0.75} > @@ -702,7 +699,7 @@ function FaqItem({ question, answer }: { question: string; answer: string }) { - + {open && ( diff --git a/apps/rebreak-native/components/blocker/ProtectionLockedCard.tsx b/apps/rebreak-native/components/blocker/ProtectionLockedCard.tsx index fe369a7..74d952f 100644 --- a/apps/rebreak-native/components/blocker/ProtectionLockedCard.tsx +++ b/apps/rebreak-native/components/blocker/ProtectionLockedCard.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import type { ProtectionState } from '../../lib/protection'; @@ -74,12 +74,10 @@ export function ProtectionLockedCard({ state, onPressSettings }: Props) { - ({ - opacity: pressed ? 0.6 : 1, - })} + activeOpacity={0.6} accessibilityLabel={t('blocker.protection_settings_a11y')} > - + {/* Stats nur wenn aktiv und kein Cooldown */} diff --git a/apps/rebreak-native/components/chat/ChatBubble.tsx b/apps/rebreak-native/components/chat/ChatBubble.tsx index 67a9ea5..2315da0 100644 --- a/apps/rebreak-native/components/chat/ChatBubble.tsx +++ b/apps/rebreak-native/components/chat/ChatBubble.tsx @@ -3,6 +3,7 @@ import { View, Text, Pressable, + TouchableOpacity, Image, StyleSheet, Modal, @@ -127,10 +128,11 @@ export function ChatBubble({ > {/* Reply preview */} {msg.replyTo && ( - { /* could implement scroll-to */ }} + activeOpacity={0.7} style={[ styles.replyPreview, { @@ -163,13 +165,14 @@ export function ChatBubble({ )}{' '} {msg.replyTo.content || (replyHasAttachment ? t('chat.image_attachment') : '…')} - + )} {/* Image attachment */} {msg.attachmentUrl && msg.attachmentType === 'image' && ( - onOpenImage(msg.attachmentUrl!)} + activeOpacity={0.7} style={[styles.imageWrap, msg.content ? { marginBottom: 4 } : null]} > {formatTime(msg.createdAt)} )} - + )} {/* File attachment */} @@ -284,25 +287,27 @@ export function ChatBubble({ animationType="fade" onRequestClose={() => setActionsOpen(false)} > - setActionsOpen(false)}> + setActionsOpen(false)} activeOpacity={1}> {}}> - { setActionsOpen(false); onReply(msg); }} + activeOpacity={0.7} > {t('chat.reply')} - - + { setActionsOpen(false); onLike(msg); }} + activeOpacity={0.7} > {msg.likedByMe ? t('chat.unlike') : t('chat.like')} - + {msg.content !== '' && ( - + {t('chat.copy')} - + )} - + ); diff --git a/apps/rebreak-native/components/chat/ChatInput.tsx b/apps/rebreak-native/components/chat/ChatInput.tsx index 9c3ef5e..63c5e9a 100644 --- a/apps/rebreak-native/components/chat/ChatInput.tsx +++ b/apps/rebreak-native/components/chat/ChatInput.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, Image, StyleSheet, ActivityIndicator, @@ -155,9 +155,9 @@ export function ChatInput({ {replyTo.content || '…'} - + - + )} @@ -174,21 +174,22 @@ export function ChatInput({ {attachment.name} - + - + )} {/* Input row */} - - + - )} - + ); diff --git a/apps/rebreak-native/components/chat/CreateRoomSheet.tsx b/apps/rebreak-native/components/chat/CreateRoomSheet.tsx index 4744cfa..31a9da2 100644 --- a/apps/rebreak-native/components/chat/CreateRoomSheet.tsx +++ b/apps/rebreak-native/components/chat/CreateRoomSheet.tsx @@ -3,7 +3,7 @@ import { View, Text, TextInput, - Pressable, + TouchableOpacity, StyleSheet, ActivityIndicator, } from 'react-native'; @@ -96,12 +96,12 @@ export function CreateRoomSheet({ visible, onClose, onCreated }: Props) { /> {/* Public toggle */} - setIsPublic((v) => !v)}> + setIsPublic((v) => !v)}> {t('chat.public_room')} - + {/* Join mode (private only) */} {!isPublic && ( @@ -109,8 +109,9 @@ export function CreateRoomSheet({ visible, onClose, onCreated }: Props) { {t('chat.join_mode')} {(['approval', 'invite_only'] as const).map((mode) => ( - setJoinMode(mode)} > @@ -122,7 +123,7 @@ export function CreateRoomSheet({ visible, onClose, onCreated }: Props) { > {t(`chat.join_mode_${mode === 'approval' ? 'approval' : 'invite'}`)} - + ))} @@ -132,10 +133,11 @@ export function CreateRoomSheet({ visible, onClose, onCreated }: Props) { {/* Actions */} - + {t('common.cancel')} - - + {t('chat.create')} )} - + diff --git a/apps/rebreak-native/components/chat/RoomCard.tsx b/apps/rebreak-native/components/chat/RoomCard.tsx index 5ef7e86..ef7d66f 100644 --- a/apps/rebreak-native/components/chat/RoomCard.tsx +++ b/apps/rebreak-native/components/chat/RoomCard.tsx @@ -40,8 +40,7 @@ export function RoomCard({ room, onPress }: Props) { return ( - {({ pressed }) => ( - + - - )} + ); } diff --git a/apps/rebreak-native/components/devices/AddMacSheet.tsx b/apps/rebreak-native/components/devices/AddMacSheet.tsx index 9867d6d..8beb231 100644 --- a/apps/rebreak-native/components/devices/AddMacSheet.tsx +++ b/apps/rebreak-native/components/devices/AddMacSheet.tsx @@ -2,7 +2,7 @@ import { ActivityIndicator, Alert, Linking, - Pressable, + TouchableOpacity, Text, TextInput, View, @@ -140,13 +140,13 @@ export function AddMacSheet({ ? t('devices.download_button') : t('devices.success_title')} - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + } > @@ -221,16 +221,17 @@ function Step1LabelContent({ ) : null} - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, alignItems: 'center', - opacity: pressed || enrolling ? 0.7 : 1, - })} + opacity: enrolling ? 0.7 : 1, + }} > {enrolling ? ( @@ -239,7 +240,7 @@ function Step1LabelContent({ {t('devices.prepare_profile')} )} - + ); } @@ -330,9 +331,10 @@ function Step2OnboardingContent({ {/* Download button */} - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, @@ -340,27 +342,27 @@ function Step2OnboardingContent({ flexDirection: 'row', justifyContent: 'center', gap: 8, - opacity: pressed ? 0.7 : 1, - })} + }} > {t('devices.download_button')} - + {/* Confirm installed */} - ({ + activeOpacity={0.7} + style={{ borderWidth: 1.5, borderColor: colors.brandOrange, borderRadius: 14, paddingVertical: 14, alignItems: 'center', - opacity: pressed || confirming ? 0.7 : 1, - })} + opacity: confirming ? 0.7 : 1, + }} > {confirming ? ( @@ -369,12 +371,13 @@ function Step2OnboardingContent({ {t('devices.confirm_installed')} )} - + {/* Need help */} - ({ opacity: pressed ? 0.5 : 1, alignItems: 'center' })} + activeOpacity={0.5} + style={{ alignItems: 'center' }} > {t('devices.need_help')} - + ); } @@ -436,22 +439,22 @@ function Step3SuccessContent({ - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, paddingHorizontal: 40, alignItems: 'center', - opacity: pressed ? 0.7 : 1, alignSelf: 'stretch', - })} + }} > {t('common.ok')} - + ); } diff --git a/apps/rebreak-native/components/devices/AddWindowsSheet.tsx b/apps/rebreak-native/components/devices/AddWindowsSheet.tsx index ced504f..5935c19 100644 --- a/apps/rebreak-native/components/devices/AddWindowsSheet.tsx +++ b/apps/rebreak-native/components/devices/AddWindowsSheet.tsx @@ -2,7 +2,7 @@ import { ActivityIndicator, Alert, Linking, - Pressable, + TouchableOpacity, Text, TextInput, View, @@ -141,13 +141,13 @@ export function AddWindowsSheet({ ? t('devices.windows_download_button') : t('devices.windows_success_title')} - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + } > @@ -228,16 +228,17 @@ function WindowsStep1LabelContent({ ) : null} - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, alignItems: 'center', - opacity: pressed || enrolling ? 0.7 : 1, - })} + opacity: enrolling ? 0.7 : 1, + }} > {enrolling ? ( @@ -246,7 +247,7 @@ function WindowsStep1LabelContent({ {t('devices.prepare_profile')} )} - + ); } @@ -337,9 +338,10 @@ function WindowsStep2OnboardingContent({ {/* Download button */} - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, @@ -347,27 +349,27 @@ function WindowsStep2OnboardingContent({ flexDirection: 'row', justifyContent: 'center', gap: 8, - opacity: pressed ? 0.7 : 1, - })} + }} > {t('devices.windows_download_button')} - + {/* Confirm installed */} - ({ + activeOpacity={0.7} + style={{ borderWidth: 1.5, borderColor: colors.brandOrange, borderRadius: 14, paddingVertical: 14, alignItems: 'center', - opacity: pressed || confirming ? 0.7 : 1, - })} + opacity: confirming ? 0.7 : 1, + }} > {confirming ? ( @@ -376,12 +378,13 @@ function WindowsStep2OnboardingContent({ {t('devices.confirm_installed')} )} - + {/* Need help */} - ({ opacity: pressed ? 0.5 : 1, alignItems: 'center' })} + activeOpacity={0.5} + style={{ alignItems: 'center' }} > {t('devices.need_help')} - + ); } @@ -443,22 +446,22 @@ function WindowsStep3SuccessContent({ - ({ + activeOpacity={0.7} + style={{ backgroundColor: colors.brandOrange, borderRadius: 14, paddingVertical: 16, paddingHorizontal: 40, alignItems: 'center', - opacity: pressed ? 0.7 : 1, alignSelf: 'stretch', - })} + }} > {t('common.ok')} - + ); } diff --git a/apps/rebreak-native/components/games/GameCard.tsx b/apps/rebreak-native/components/games/GameCard.tsx index 6b4c1f7..5742ffc 100644 --- a/apps/rebreak-native/components/games/GameCard.tsx +++ b/apps/rebreak-native/components/games/GameCard.tsx @@ -1,6 +1,6 @@ // RN-Port der Vue-Card aus apps/rebreak/app/components/urge/UrgeGamePicker.vue // 2x2-Grid-Kachel mit SVG-Icon (56x56), Titel, descKey und Star-Rating. -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { SvgXml } from 'react-native-svg'; import { useTranslation } from 'react-i18next'; import { GameRatingStars } from './GameRatingStars'; @@ -27,13 +27,10 @@ export function GameCard({ }: GameCardProps) { const { t } = useTranslation(); return ( - onPress(id)} - style={({ pressed }) => ({ - width: '100%', - transform: [{ scale: pressed ? 0.97 : 1 }], - opacity: pressed ? 0.85 : 1, - })} + activeOpacity={0.85} + style={{ width: '100%' }} > - + ); } diff --git a/apps/rebreak-native/components/games/StarRating.tsx b/apps/rebreak-native/components/games/StarRating.tsx index 271f761..4517dda 100644 --- a/apps/rebreak-native/components/games/StarRating.tsx +++ b/apps/rebreak-native/components/games/StarRating.tsx @@ -1,6 +1,6 @@ // RN-Port von apps/rebreak/app/components/StarRating.vue // Unterstützt fractional values (z.B. 3.7) via width-clipping. -import { View, Pressable } from 'react-native'; +import { View, TouchableOpacity } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useState } from 'react'; @@ -83,15 +83,14 @@ export function StarRating({ if (interactive) { stars.push( - onChange?.(i)} - onHoverIn={() => setHover(i)} - onHoverOut={() => setHover(0)} hitSlop={4} + activeOpacity={0.7} > {star} - + ); } else { stars.push(star); diff --git a/apps/rebreak-native/components/header/HeaderDropdownMenu.tsx b/apps/rebreak-native/components/header/HeaderDropdownMenu.tsx index 01a41de..6bfb8e6 100644 --- a/apps/rebreak-native/components/header/HeaderDropdownMenu.tsx +++ b/apps/rebreak-native/components/header/HeaderDropdownMenu.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable, Modal } from 'react-native'; +import { View, Text, Pressable, TouchableOpacity, Modal } from 'react-native'; import { useRouter, type RelativePathString } from 'expo-router'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; @@ -166,14 +166,13 @@ export function HeaderDropdownMenu({ visible, onClose, topOffset = 80 }: Props) {/* Profile · Settings · Games · [Debug DEV] */} {items.map((item) => ( - { onClose(); void item.onSelect(); }} - android_ripple={{ color: colors.surfaceElevated }} - style={({ pressed }) => ({ opacity: pressed ? 0.7 : 1 })} + activeOpacity={0.7} > - + ))} {/* Abmelden — neutral, nicht rot */} - ({ opacity: pressed ? 0.7 : 1 })} + activeOpacity={0.7} > - + diff --git a/apps/rebreak-native/components/mail/ConnectMailSheet.tsx b/apps/rebreak-native/components/mail/ConnectMailSheet.tsx index 664b399..1855956 100644 --- a/apps/rebreak-native/components/mail/ConnectMailSheet.tsx +++ b/apps/rebreak-native/components/mail/ConnectMailSheet.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import { ActivityIndicator, Linking, - Pressable, + TouchableOpacity, ScrollView, Text, TextInput, @@ -164,17 +164,17 @@ export function ConnectMailSheet({ visible, onClose, onSuccess }: Props) { }} > {view === 'form' ? ( - + {t('common.back')} - + ) : ( - + {t('common.cancel')} - + )} {view === 'form' && currentProvider @@ -250,13 +250,11 @@ function ProviderGrid({ {providers.map((p) => ( - onSelect(p)} - style={({ pressed }) => ({ - width: '47%', - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ width: '47%' }} > - + ))} @@ -376,7 +374,7 @@ function FormView({ {t(provider.guideKey)} {provider.guideUrl.length > 0 && ( - Linking.openURL(provider.guideUrl)}> + Linking.openURL(provider.guideUrl)}> {t('mail.app_password_open_link')} → - + )} @@ -460,7 +458,8 @@ function FormView({ color: colors.text, }} /> - - + @@ -520,14 +519,11 @@ function FormView({ )} {/* Connect-Button */} - ({ - opacity: pressed ? 0.85 : 1, - marginTop: 4, - marginBottom: insets.bottom > 0 ? 8 : 12, - })} + style={{ marginTop: 4, marginBottom: insets.bottom > 0 ? 8 : 12 }} > )} - + ); } diff --git a/apps/rebreak-native/components/mail/EditMailAccountSheet.tsx b/apps/rebreak-native/components/mail/EditMailAccountSheet.tsx index 55e3c84..e0d11aa 100644 --- a/apps/rebreak-native/components/mail/EditMailAccountSheet.tsx +++ b/apps/rebreak-native/components/mail/EditMailAccountSheet.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { ActivityIndicator, Pressable, Text, TextInput, View } from 'react-native'; +import { ActivityIndicator, TouchableOpacity, Text, TextInput, View } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useTranslation } from 'react-i18next'; import { useMailConnect } from '../../hooks/useMailConnect'; @@ -64,11 +64,11 @@ export function EditMailAccountSheet({ visible, email, onClose, onSuccess }: Pro borderBottomColor: colors.border, }} > - + {t('mail.edit_account_cancel')} - + {t('mail.edit_account_title')} @@ -125,13 +125,13 @@ export function EditMailAccountSheet({ visible, email, onClose, onSuccess }: Pro color: colors.text, }} /> - setPasswordVisible((p) => !p)} hitSlop={8}> + setPasswordVisible((p) => !p)} hitSlop={8}> - + {(formError ?? connectError) && ( @@ -161,13 +161,11 @@ export function EditMailAccountSheet({ visible, email, onClose, onSuccess }: Pro )} - ({ - marginTop: 4, - opacity: pressed ? 0.85 : 1, - })} + style={{ marginTop: 4 }} > )} - + ); diff --git a/apps/rebreak-native/components/mail/MailAccountCard.tsx b/apps/rebreak-native/components/mail/MailAccountCard.tsx index c6ebbe4..4058674 100644 --- a/apps/rebreak-native/components/mail/MailAccountCard.tsx +++ b/apps/rebreak-native/components/mail/MailAccountCard.tsx @@ -4,6 +4,7 @@ import { LayoutAnimation, Platform, Pressable, + TouchableOpacity, Text, UIManager, View, @@ -408,8 +409,9 @@ export function MailAccountCard({ const active = account.scanInterval === opt; const disabled = plan === 'free' || updating === account.id; return ( - handleSetInterval(opt)} style={{ @@ -431,7 +433,7 @@ export function MailAccountCard({ > {opt}h - + ); })} @@ -451,7 +453,8 @@ export function MailAccountCard({ )} - setEditVisible(true)} style={{ ...ACTION_BTN_BASE, backgroundColor: '#f5f5f5', marginRight: 6 }} > @@ -467,8 +470,9 @@ export function MailAccountCard({ > {t('mail.account_change_password')} - - + setConfirmVisible(true)} disabled={disconnecting} style={{ @@ -496,7 +500,7 @@ export function MailAccountCard({ )} - + )} diff --git a/apps/rebreak-native/components/mail/MailActivityLog.tsx b/apps/rebreak-native/components/mail/MailActivityLog.tsx index de75ffe..72e3ce7 100644 --- a/apps/rebreak-native/components/mail/MailActivityLog.tsx +++ b/apps/rebreak-native/components/mail/MailActivityLog.tsx @@ -2,6 +2,7 @@ import { LayoutAnimation, Platform, Pressable, + TouchableOpacity, Text, UIManager, View, @@ -140,9 +141,9 @@ export function MailActivityLog({ expanded, onToggle }: Props) { ? t('mail.activity_log_more', { count: total - 10 }) : t('mail.activity_log_count', { count: total })} - + - + )} diff --git a/apps/rebreak-native/components/mail/MailWeeklyChart.tsx b/apps/rebreak-native/components/mail/MailWeeklyChart.tsx index a183aff..6ee4783 100644 --- a/apps/rebreak-native/components/mail/MailWeeklyChart.tsx +++ b/apps/rebreak-native/components/mail/MailWeeklyChart.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { Pressable, Text, View } from 'react-native'; +import { TouchableOpacity, Text, View } from 'react-native'; import Svg, { Rect, Text as SvgText } from 'react-native-svg'; import { useTranslation } from 'react-i18next'; import { useColors } from '../../lib/theme'; @@ -145,10 +145,11 @@ export function MailWeeklyChart({ dailyStats, totalBlocked }: Props) { }} > {dailyStats.map((day, i) => ( - setActiveIdx((prev) => (prev === i ? null : i))} + activeOpacity={0.7} accessibilityLabel={`${day.label}: ${day.count}`} /> ))} diff --git a/apps/rebreak-native/components/profile/ApprovedDomainsList.tsx b/apps/rebreak-native/components/profile/ApprovedDomainsList.tsx index a4eaefd..0cfa7bf 100644 --- a/apps/rebreak-native/components/profile/ApprovedDomainsList.tsx +++ b/apps/rebreak-native/components/profile/ApprovedDomainsList.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { View, Text, Pressable, LayoutAnimation, Platform, UIManager } from 'react-native'; +import { View, Text, TouchableOpacity, LayoutAnimation, Platform, UIManager } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useColors } from '../../lib/theme'; @@ -28,9 +28,9 @@ export function ApprovedDomainsList({ domains, loading }: Props) { return ( - ({ opacity: pressed ? 0.7 : 1 })} + activeOpacity={0.7} > - + {expanded ? ( - ({ opacity: pressed ? 0.7 : 1 })} + activeOpacity={0.7} > )} - + {expanded ? ( - ({ opacity: pressed ? 0.6 : 1 })} + activeOpacity={0.6} > - + @@ -637,9 +637,9 @@ export function DemographicsAccordion({ ) : null} - ({ opacity: pressed ? 0.7 : 1 })} + activeOpacity={0.7} > - + ) : null} @@ -753,9 +753,9 @@ function FieldRow({ function SelectButton({ value, onPress }: { value: string | null; onPress: () => void }) { const colors = useColors(); return ( - ({ opacity: pressed ? 0.6 : 1 })} + activeOpacity={0.6} > {/* Value als Chip */} @@ -782,6 +782,6 @@ function SelectButton({ value, onPress }: { value: string | null; onPress: () => {/* Chevron-right am Ende, separat vom Chip */} - + ); } diff --git a/apps/rebreak-native/components/profile/DigaMissionBanner.tsx b/apps/rebreak-native/components/profile/DigaMissionBanner.tsx index 1aa68bd..bb647ef 100644 --- a/apps/rebreak-native/components/profile/DigaMissionBanner.tsx +++ b/apps/rebreak-native/components/profile/DigaMissionBanner.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useColors } from '../../lib/theme'; @@ -60,11 +60,9 @@ export function DigaMissionBanner({ onDismiss, onContribute }: Props) { - ({ - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} > - - + ({ - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} > - + - ({ opacity: pressed ? 0.5 : 1 })} + activeOpacity={0.5} > - + ); diff --git a/apps/rebreak-native/components/profile/ProfileHeader.tsx b/apps/rebreak-native/components/profile/ProfileHeader.tsx index 26986bb..6a5ff4a 100644 --- a/apps/rebreak-native/components/profile/ProfileHeader.tsx +++ b/apps/rebreak-native/components/profile/ProfileHeader.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { View, Text, Pressable, Image } from 'react-native'; +import { View, Text, TouchableOpacity, Image } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import Svg, { Path } from 'react-native-svg'; import { useColors } from '../../lib/theme'; @@ -84,12 +84,10 @@ export function ProfileHeader({ {/* Avatar — Pressable in alignSelf:center-Wrapper (Pressable+style-fn ignoriert alignSelf manchmal in RN) */} - ({ - position: 'relative', - opacity: pressed ? 0.85 : 1, - })} + activeOpacity={0.85} + style={{ position: 'relative' }} > - + {/* Nickname — ganze Zeile Pressable in alignSelf:center-Wrapper */} - ({ + activeOpacity={0.5} + style={{ flexDirection: 'row', alignItems: 'center', marginTop: 16, gap: 6, - opacity: pressed ? 0.5 : 1, - })} + }} > {nickname} - + {/* Plan-Tier-Badge direkt unter Nickname — Legend mit sparkles-icon */} @@ -249,14 +247,11 @@ export function ProfileHeader({ Parent ist KEIN alignItems:center mehr — Hint nimmt natürlich volle Breite (default flex-stretch). KEIN width:'100%' (Konflikt mit alignSelf:stretch in alignItems:center-Kontext war der Bug). */} {showDemographicsHint ? ( - ({ - alignSelf: 'center', - marginTop: 16, - opacity: pressed ? 0.7 : 1, - })} + activeOpacity={0.7} + style={{ alignSelf: 'center', marginTop: 16 }} > - + ) : null} ); diff --git a/apps/rebreak-native/components/profile/StatsBar.tsx b/apps/rebreak-native/components/profile/StatsBar.tsx index 0755572..f42a615 100644 --- a/apps/rebreak-native/components/profile/StatsBar.tsx +++ b/apps/rebreak-native/components/profile/StatsBar.tsx @@ -1,4 +1,4 @@ -import { View, Text, Pressable } from 'react-native'; +import { View, Text, TouchableOpacity } from 'react-native'; import { useColors } from '../../lib/theme'; type Props = { @@ -19,9 +19,9 @@ type CardProps = { function StatPill({ value, label, onPress }: CardProps) { const colors = useColors(); return ( - ({ opacity: pressed ? 0.6 : 1 })} + activeOpacity={0.6} > - + ); } diff --git a/apps/rebreak-native/components/urge/Breathing.tsx b/apps/rebreak-native/components/urge/Breathing.tsx index e7f030e..b6f5575 100644 --- a/apps/rebreak-native/components/urge/Breathing.tsx +++ b/apps/rebreak-native/components/urge/Breathing.tsx @@ -1,6 +1,6 @@ // 4-7-8 Atemübung: Card (in-chat) + Drawer (bottom sheet). import { useEffect, useRef, useState } from 'react'; -import { View, Text, Pressable, Animated, StyleSheet } from 'react-native'; +import { View, Text, TouchableOpacity, Animated, StyleSheet } from 'react-native'; import { BREATH_PHASES, TOTAL_ROUNDS, type BreathState } from '../../lib/sosConstants'; import { useColors } from '../../lib/theme'; @@ -87,9 +87,9 @@ export function BreathingCard({ onDone, onSpeak }: Props) { 4-7-8 Atemübung 3 Runden · beruhigt dein Nervensystem - { setCountdown(3); setBreathState('countdown'); }}> + { setCountdown(3); setBreathState('countdown'); }}> Starten - + ) : breathState === 'countdown' ? ( diff --git a/apps/rebreak-native/components/urge/InlineRatingDrawer.tsx b/apps/rebreak-native/components/urge/InlineRatingDrawer.tsx index a063a58..5ed522d 100644 --- a/apps/rebreak-native/components/urge/InlineRatingDrawer.tsx +++ b/apps/rebreak-native/components/urge/InlineRatingDrawer.tsx @@ -3,6 +3,7 @@ import { View, Text, Pressable, + TouchableOpacity, TextInput, StyleSheet, Animated, @@ -80,7 +81,8 @@ export function InlineRatingDrawer({ Fühlst du dich besser? - setBetter(true)} > @@ -90,8 +92,9 @@ export function InlineRatingDrawer({ color={better === true ? '#fff' : '#16a34a'} /> Ja - - + setBetter(false)} > @@ -101,19 +104,19 @@ export function InlineRatingDrawer({ color={better === false ? '#fff' : '#dc2626'} /> Nein - + Bewertung {[1, 2, 3, 4, 5].map((n) => ( - setRating(n)} hitSlop={6}> + setRating(n)} hitSlop={6} activeOpacity={0.7}> - + ))} @@ -129,16 +132,17 @@ export function InlineRatingDrawer({ /> - + Abbrechen - - + {submitting ? 'Sende…' : 'Senden'} - + diff --git a/apps/rebreak-native/components/urge/LlmProviderToggle.tsx b/apps/rebreak-native/components/urge/LlmProviderToggle.tsx index ff7f968..018a1d6 100644 --- a/apps/rebreak-native/components/urge/LlmProviderToggle.tsx +++ b/apps/rebreak-native/components/urge/LlmProviderToggle.tsx @@ -1,4 +1,4 @@ -import { Pressable, Text, View } from 'react-native'; +import { TouchableOpacity, Text, View } from 'react-native'; import { LLM_PROVIDER_LABEL, type LlmProvider, useLlmProvider } from '../../lib/llmProvider'; const PROVIDERS: LlmProvider[] = ['auto', 'openrouter-sonnet', 'openrouter-haiku', 'groq-llama']; @@ -30,10 +30,11 @@ export function LlmProviderToggle() { {PROVIDERS.map((p) => { const active = p === current; return ( - { void set(p); }} hitSlop={6} + activeOpacity={0.7} style={{ paddingHorizontal: 10, paddingVertical: 4, @@ -52,7 +53,7 @@ export function LlmProviderToggle() { > {LLM_PROVIDER_LABEL[p]} - + ); })} diff --git a/apps/rebreak-native/components/urge/MessageRow.tsx b/apps/rebreak-native/components/urge/MessageRow.tsx index 421e303..47bbd6b 100644 --- a/apps/rebreak-native/components/urge/MessageRow.tsx +++ b/apps/rebreak-native/components/urge/MessageRow.tsx @@ -1,6 +1,6 @@ // Chat-Bubble + Spezial-Cards (Spiele/Überwunden) für den SOS-Chat-Stream // sowie GameHeader für die aktive Spiel-Session. -import { View, Text, Pressable, StyleSheet } from 'react-native'; +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { type GameType, GAME_META, GamePickerGrid } from './UrgeGames'; import { RiveAvatar, type Emotion as LyraEmotion } from '../RiveAvatar'; @@ -57,7 +57,7 @@ export function GameHeader({ game, emotion, onBack }: { game: GameType; emotion: const meta = GAME_META.find((g) => g.id === game); return ( - + {meta?.id ?? game} diff --git a/apps/rebreak-native/components/urge/ShareSuccessDrawer.tsx b/apps/rebreak-native/components/urge/ShareSuccessDrawer.tsx index d470403..5709525 100644 --- a/apps/rebreak-native/components/urge/ShareSuccessDrawer.tsx +++ b/apps/rebreak-native/components/urge/ShareSuccessDrawer.tsx @@ -3,6 +3,7 @@ import { View, Text, Pressable, + TouchableOpacity, TextInput, StyleSheet, Animated, @@ -106,19 +107,21 @@ export function ShareSuccessDrawer({ {onRegenerate && ( - Neu generieren - + )} - + Abbrechen - - + Teilen )} - + diff --git a/apps/rebreak-native/components/urge/SosFeedbackModal.tsx b/apps/rebreak-native/components/urge/SosFeedbackModal.tsx index d6032c7..ab389e5 100644 --- a/apps/rebreak-native/components/urge/SosFeedbackModal.tsx +++ b/apps/rebreak-native/components/urge/SosFeedbackModal.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { View, Text, Pressable, TextInput, Modal, StyleSheet, Platform, KeyboardAvoidingView, ScrollView } from 'react-native'; +import { View, Text, Pressable, TouchableOpacity, TextInput, Modal, StyleSheet, Platform, KeyboardAvoidingView, ScrollView } from 'react-native'; import { Ionicons } from '@expo/vector-icons'; import { useColors } from '../../lib/theme'; @@ -51,33 +51,35 @@ export function SosFeedbackModal({ {/* Better Yes/No */} Fühlst du dich besser? - setBetter(true)} > Ja - - + setBetter(false)} > Nein - + {/* Stars */} Bewertung {[1, 2, 3, 4, 5].map((n) => ( - setRating(n)} hitSlop={6}> + setRating(n)} hitSlop={6} activeOpacity={0.7}> - + ))} @@ -96,12 +98,12 @@ export function SosFeedbackModal({ {/* Actions */} - + Überspringen - - + + Senden - + diff --git a/apps/rebreak-native/components/urge/TtsProviderToggle.tsx b/apps/rebreak-native/components/urge/TtsProviderToggle.tsx index fb7b4d3..c7047b1 100644 --- a/apps/rebreak-native/components/urge/TtsProviderToggle.tsx +++ b/apps/rebreak-native/components/urge/TtsProviderToggle.tsx @@ -1,4 +1,4 @@ -import { Pressable, Text, View } from 'react-native'; +import { TouchableOpacity, Text, View } from 'react-native'; import { TTS_PROVIDER_LABEL, type TtsProvider, useTtsProvider } from '../../lib/ttsProvider'; const PROVIDERS: TtsProvider[] = ['openai', 'gemini', 'elevenlabs', 'cartesia', 'google-cloud']; @@ -30,10 +30,11 @@ export function TtsProviderToggle() { {PROVIDERS.map((p) => { const active = p === current; return ( - { void set(p); }} hitSlop={6} + activeOpacity={0.7} style={{ paddingHorizontal: 10, paddingVertical: 4, @@ -52,7 +53,7 @@ export function TtsProviderToggle() { > {TTS_PROVIDER_LABEL[p]} - + ); })} diff --git a/apps/rebreak-native/components/urge/UrgeGames.tsx b/apps/rebreak-native/components/urge/UrgeGames.tsx index 3d29167..fd94598 100644 --- a/apps/rebreak-native/components/urge/UrgeGames.tsx +++ b/apps/rebreak-native/components/urge/UrgeGames.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState, useCallback } from 'react'; -import { View, Text, Pressable, TouchableWithoutFeedback, Dimensions, PanResponder, Platform } from 'react-native'; +import { View, Text, Pressable, TouchableOpacity, TouchableWithoutFeedback, Dimensions, PanResponder, Platform } from 'react-native'; import Svg, { Defs, Pattern, Path, Rect, Polyline, Circle, Line } from 'react-native-svg'; import { SvgXml } from 'react-native-svg'; import { Ionicons } from '@expo/vector-icons'; @@ -38,13 +38,11 @@ export function GamePickerGrid({ onSelect }: { onSelect: (game: GameType) => voi return ( {GAME_META.map((game) => ( - onSelect(game.id)} - style={({ pressed }) => ({ - width: '47%', - opacity: pressed ? 0.75 : 1, - })} + activeOpacity={0.75} + style={{ width: '47%' }} > voi {t(game.descKey)} - + ))} );