From 47bca948aa832cc2c1ebba95f6dcdb3079112991 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 00:19:44 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20flight?= =?UTF-8?q?=20card=20haptics=20and=20accessibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add haptic feedback to flight card swipe gestures using expo-haptics. - Implement comprehensive accessibility labels for flight cards. - Add accessibility actions (pin/unpin) for screen reader users. - Add necessary translations for new accessibility strings. Co-authored-by: TargetMisser <52361977+TargetMisser@users.noreply.github.com> --- src/i18n/translations.ts | 8 +++++++ src/screens/FlightScreen.tsx | 42 ++++++++++++++++++++++++++++++++---- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index 26c05cb..e389828 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -121,6 +121,8 @@ const it = { flightCheckin: 'Check-in', flightGate: 'Gate', flightStand: 'Stand', flightBelt: 'Nastro', flightDeparted: 'Partito', flightLanded: 'Atterrato', flightEstimated: 'Stimato', flightOnTime: 'In orario', flightPinned: 'PINNATO', flightPinnedLabel: 'Pinnato', + flightAccessibilityPin: 'Pinna volo', flightAccessibilityUnpin: 'Rimuovi pin', + flightFrom: 'da', flightTo: 'per', flightNotifEnabled: 'Notifiche attivate', flightNotifPermDenied: 'Permesso negato', flightNotifPermMsg: 'Abilita le notifiche nelle impostazioni del telefono per usare questa funzione.', @@ -129,6 +131,8 @@ const it = { flightNotifMsg1: 'Programmate {count} notifiche per il turno di oggi.', flightNotifMsg0: 'Nessuna notifica futura trovata con i filtri attivi.', flightNotifAccessEnable: 'Attiva notifiche voli', flightNotifAccessDisable: 'Disattiva notifiche voli', + flightAccessibilityPin: 'Pinna volo', flightAccessibilityUnpin: 'Rimuovi pin', + flightFrom: 'da', flightTo: 'per', flightNotifSettingsTitle: 'Impostazioni notifiche', flightNotifSettingsSub: 'Decidi cosa ricevere, sticky e minutaggi.', flightNotifOnlyTracked: 'Solo compagnie monitorate', @@ -320,6 +324,8 @@ const en: typeof it = { flightCheckin: 'Check-in', flightGate: 'Gate', flightStand: 'Stand', flightBelt: 'Belt', flightDeparted: 'Departed', flightLanded: 'Landed', flightEstimated: 'Estimated', flightOnTime: 'On time', flightPinned: 'PINNED', flightPinnedLabel: 'Pinned', + flightAccessibilityPin: 'Pin flight', flightAccessibilityUnpin: 'Unpin flight', + flightFrom: 'from', flightTo: 'to', flightNotifEnabled: 'Notifications enabled', flightNotifPermDenied: 'Permission denied', flightNotifPermMsg: 'Enable notifications in phone settings to use this feature.', @@ -328,6 +334,8 @@ const en: typeof it = { flightNotifMsg1: '{count} notifications scheduled for today’s shift.', flightNotifMsg0: 'No upcoming notifications found with current filters.', flightNotifAccessEnable: 'Enable flight notifications', flightNotifAccessDisable: 'Disable flight notifications', + flightAccessibilityPin: 'Pin flight', flightAccessibilityUnpin: 'Unpin flight', + flightFrom: 'from', flightTo: 'to', flightNotifSettingsTitle: 'Notification settings', flightNotifSettingsSub: 'Choose what to receive, sticky behavior and timing.', flightNotifOnlyTracked: 'Only tracked airlines', diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx index 09dbe5d..bc103ea 100644 --- a/src/screens/FlightScreen.tsx +++ b/src/screens/FlightScreen.tsx @@ -3,8 +3,10 @@ import { View, Text, StyleSheet, ActivityIndicator, Modal, ScrollView, FlatList, TouchableOpacity, RefreshControl, Image, Animated, PanResponder, NativeModules, Platform, Switch, + type ViewProps, type AccessibilityActionEvent, } from 'react-native'; import { Easing } from 'react-native'; +import * as Haptics from 'expo-haptics'; import * as Calendar from 'expo-calendar'; import * as Notifications from 'expo-notifications'; import AsyncStorage from '@react-native-async-storage/async-storage'; @@ -302,15 +304,17 @@ const SWIPE_MAX_TRANSLATE = 96; const SWIPE_DRAG_RESISTANCE = 0.82; function SwipeableFlightCardComponent({ - children, isPinned, onToggle, + children, isPinned, onToggle, ...rest }: { children: React.ReactNode; isPinned: boolean; onToggle: () => void; -}) { +} & ViewProps) { const translateX = useRef(new Animated.Value(0)).current; const onToggleRef = useRef(onToggle); onToggleRef.current = onToggle; + + const hasTriggeredHaptic = useRef(false); const dragScale = useMemo(() => translateX.interpolate({ inputRange: [-SWIPE_MAX_TRANSLATE, 0], outputRange: [0.985, 1], @@ -336,9 +340,17 @@ function SwipeableFlightCardComponent({ ? Math.max(g.dx * SWIPE_DRAG_RESISTANCE, -SWIPE_MAX_TRANSLATE) : g.dx * 0.08; translateX.setValue(nextTranslate); + + if (nextTranslate <= -SWIPE_THRESHOLD && !hasTriggeredHaptic.current) { + Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Medium); + hasTriggeredHaptic.current = true; + } else if (nextTranslate > -SWIPE_THRESHOLD && hasTriggeredHaptic.current) { + hasTriggeredHaptic.current = false; + } }, onPanResponderRelease: (_, g) => { if (g.dx < -SWIPE_THRESHOLD || g.vx < -SWIPE_TRIGGER_VELOCITY) { + Haptics.notificationAsync(Haptics.NotificationFeedbackType.Success); Animated.timing(translateX, { toValue: -SWIPE_MAX_TRANSLATE, duration: 170, @@ -351,6 +363,7 @@ function SwipeableFlightCardComponent({ } else { animateBack(g.vx); } + hasTriggeredHaptic.current = false; }, onPanResponderTerminate: () => { animateBack(); @@ -359,7 +372,11 @@ function SwipeableFlightCardComponent({ return ( - + {children} @@ -515,12 +532,29 @@ function FlightRowComponent({ item, activeTab, userShift, pinnedFlightId, onPin, return () => clearInterval(interval); }, []); + const aLabel = useMemo(() => { + const direction = activeTab === 'arrivals' ? t('flightFrom') : t('flightTo'); + const pinnedState = isPinned ? `${t('flightPinnedLabel')}. ` : ''; + return `${pinnedState}${flightNumber}, ${airline}, ${direction} ${originDest}, ${time}, ${statusText}`; + }, [activeTab, airline, flightNumber, isPinned, originDest, statusText, t, time]); + + const handleAccessibilityAction = useCallback((event: AccessibilityActionEvent) => { + if (event.nativeEvent.actionName === 'togglePin') { + if (isPinned) onUnpin(); + else onPin(item); + } + }, [isPinned, onPin, onUnpin, item]); + return ( isPinned ? onUnpin() : onPin(item)} + accessible + accessibilityLabel={aLabel} + accessibilityActions={[{ name: 'togglePin', label: isPinned ? t('flightAccessibilityUnpin') : t('flightAccessibilityPin') }]} + onAccessibilityAction={handleAccessibilityAction} > - + {isPinned && {t('flightPinned')}} {/* Header */} From d4a3794a579fd916a761c6cddf0da81140221742 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 1 May 2026 00:36:35 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=F0=9F=8E=A8=20Palette:=20Enhance=20flight?= =?UTF-8?q?=20card=20haptics=20and=20accessibility?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add haptic feedback to flight card swipe gestures using expo-haptics. - Implement comprehensive accessibility labels for flight cards. - Add accessibility actions (pin/unpin) for screen reader users. - Add necessary translations for new accessibility strings. Co-authored-by: TargetMisser <52361977+TargetMisser@users.noreply.github.com> --- src/i18n/translations.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/i18n/translations.ts b/src/i18n/translations.ts index e389828..f3982df 100644 --- a/src/i18n/translations.ts +++ b/src/i18n/translations.ts @@ -121,8 +121,6 @@ const it = { flightCheckin: 'Check-in', flightGate: 'Gate', flightStand: 'Stand', flightBelt: 'Nastro', flightDeparted: 'Partito', flightLanded: 'Atterrato', flightEstimated: 'Stimato', flightOnTime: 'In orario', flightPinned: 'PINNATO', flightPinnedLabel: 'Pinnato', - flightAccessibilityPin: 'Pinna volo', flightAccessibilityUnpin: 'Rimuovi pin', - flightFrom: 'da', flightTo: 'per', flightNotifEnabled: 'Notifiche attivate', flightNotifPermDenied: 'Permesso negato', flightNotifPermMsg: 'Abilita le notifiche nelle impostazioni del telefono per usare questa funzione.', @@ -324,8 +322,6 @@ const en: typeof it = { flightCheckin: 'Check-in', flightGate: 'Gate', flightStand: 'Stand', flightBelt: 'Belt', flightDeparted: 'Departed', flightLanded: 'Landed', flightEstimated: 'Estimated', flightOnTime: 'On time', flightPinned: 'PINNED', flightPinnedLabel: 'Pinned', - flightAccessibilityPin: 'Pin flight', flightAccessibilityUnpin: 'Unpin flight', - flightFrom: 'from', flightTo: 'to', flightNotifEnabled: 'Notifications enabled', flightNotifPermDenied: 'Permission denied', flightNotifPermMsg: 'Enable notifications in phone settings to use this feature.',