Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/i18n/translations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,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',
Comment on lines +132 to +133
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove duplicate translation keys from locale maps

The new flightAccessibilityPin, flightAccessibilityUnpin, flightFrom, and flightTo entries are declared twice in each locale object, which introduces TypeScript duplicate-property errors (TS1117) and can break CI/builds that run tsc --noEmit; at runtime, the later declarations also silently overwrite earlier ones. This was introduced in this commit and should be deduplicated in both it and en maps.

Useful? React with 👍 / 👎.

flightNotifSettingsTitle: 'Impostazioni notifiche',
flightNotifSettingsSub: 'Decidi cosa ricevere, sticky e minutaggi.',
flightNotifOnlyTracked: 'Solo compagnie monitorate',
Expand Down Expand Up @@ -328,6 +330,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',
Expand Down
42 changes: 38 additions & 4 deletions src/screens/FlightScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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],
Expand All @@ -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,
Expand All @@ -351,6 +363,7 @@ function SwipeableFlightCardComponent({
} else {
animateBack(g.vx);
}
hasTriggeredHaptic.current = false;
},
onPanResponderTerminate: () => {
animateBack();
Expand All @@ -359,7 +372,11 @@ function SwipeableFlightCardComponent({

return (
<View style={{ marginBottom: 10 }}>
<Animated.View style={{ transform: [{ translateX }, { scale: dragScale }] }} {...panResponder.panHandlers}>
<Animated.View
style={{ transform: [{ translateX }, { scale: dragScale }] }}
{...panResponder.panHandlers}
{...rest}
>
{children}
</Animated.View>
</View>
Expand Down Expand Up @@ -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 (
<SwipeableFlightCard
isPinned={isPinned}
onToggle={() => isPinned ? onUnpin() : onPin(item)}
accessible
accessibilityLabel={aLabel}
accessibilityActions={[{ name: 'togglePin', label: isPinned ? t('flightAccessibilityUnpin') : t('flightAccessibilityPin') }]}
onAccessibilityAction={handleAccessibilityAction}
>
<View style={[s.card, isPinned && s.cardPinned, { marginBottom: 0 }]}>
<View style={[s.card, isPinned && s.cardPinned, { marginBottom: 0 }]} importantForAccessibility="no-hide-descendants">
{isPinned && <View style={s.pinBanner}><Text style={s.pinBannerText}>{t('flightPinned')}</Text></View>}
{/* Header */}
<View style={[s.cardHeader, { backgroundColor: color }]}>
Expand Down
Loading