From efc23ab244ba74deecce0c0757b778e2fba2ad86 Mon Sep 17 00:00:00 2001
From: TargetMisser <52361977+TargetMisser@users.noreply.github.com>
Date: Mon, 6 Apr 2026 17:12:53 +0200
Subject: [PATCH 1/2] refactor: design & code quality refresh
- Fix DrawerMenu: icon color hardcoded #2563EB -> colors.primary (theme-aware)
- Fix DrawerMenu: version label 'v1.0' -> 'v1.1.0' (matches package.json)
- Remove CalendarScreen: module-level const PRIMARY = '#2563EB', replaced with colors.primary
- Fix NotepadScreen: saveBtnDim hardcoded backgroundColor -> opacity: 0.55 (works in all themes)
- Replace all makeStyles(c: any) -> makeStyles(c: ThemeColors) across 8 files (14 occurrences):
DrawerMenu, ShiftTimeline, NotepadScreen, FlightScreen, HomeScreen, CalendarScreen,
PhonebookScreen (x3), PasswordScreen (x3), ManualsScreen (x3)
- Add 'type ThemeColors' to imports in all affected files
- Wrap all console.error/warn in if (__DEV__) guard (20+ occurrences):
CalendarScreen (x6), FlightScreen (x5), HomeScreen (x4), PasswordScreen (x3),
autoNotifications.ts (x3), useDynamicTheme.ts (x1)
- App.tsx: guard auto-notification count log with __DEV__
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
App.tsx | 2 +-
src/components/DrawerMenu.tsx | 8 ++++----
src/components/ShiftTimeline.tsx | 4 ++--
src/hooks/useDynamicTheme.ts | 2 +-
src/screens/CalendarScreen.tsx | 19 +++++++++----------
src/screens/FlightScreen.tsx | 14 +++++++-------
src/screens/HomeScreen.tsx | 12 ++++++------
src/screens/ManualsScreen.tsx | 8 ++++----
src/screens/NotepadScreen.tsx | 6 +++---
src/screens/PasswordScreen.tsx | 14 +++++++-------
src/screens/PhonebookScreen.tsx | 8 ++++----
src/utils/autoNotifications.ts | 6 +++---
12 files changed, 51 insertions(+), 52 deletions(-)
diff --git a/App.tsx b/App.tsx
index 4798824..3047a94 100644
--- a/App.tsx
+++ b/App.tsx
@@ -87,7 +87,7 @@ function AppInner() {
// ─── Auto-schedule flight notifications on startup ─────────────────────────
useEffect(() => {
autoScheduleNotifications().then(count => {
- if (count > 0) console.log(`Auto-scheduled ${count} notifications`);
+ if (count > 0 && __DEV__) console.log(`Auto-scheduled ${count} notifications`);
}).catch(() => {});
}, []);
diff --git a/src/components/DrawerMenu.tsx b/src/components/DrawerMenu.tsx
index 932d200..ebb6384 100644
--- a/src/components/DrawerMenu.tsx
+++ b/src/components/DrawerMenu.tsx
@@ -3,7 +3,7 @@ import {
Animated, Modal, StyleSheet, Text, TouchableOpacity, View,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
type DrawerItem = {
id: string;
@@ -89,7 +89,7 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) {
activeOpacity={0.7}
>
-
+
{item.label}
@@ -103,14 +103,14 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) {
{/* Divider */}
- AeroStaff Pro · v1.0
+ AeroStaff Pro · v1.1.0
);
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
root: { flex: 1, flexDirection: 'row' },
overlay: { ...StyleSheet.absoluteFillObject, backgroundColor: 'rgba(15,23,42,0.5)' },
diff --git a/src/components/ShiftTimeline.tsx b/src/components/ShiftTimeline.tsx
index 902c665..b989dd3 100644
--- a/src/components/ShiftTimeline.tsx
+++ b/src/components/ShiftTimeline.tsx
@@ -4,7 +4,7 @@ import {
ActivityIndicator, Dimensions, LayoutAnimation, Platform, UIManager,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
import { useAirport } from '../context/AirportContext';
import { getAirlineOps, getAirlineColor } from '../utils/airlineOps';
import { fetchAirportScheduleRaw } from '../utils/fr24api';
@@ -300,7 +300,7 @@ export default function ShiftTimeline({ visible, onClose, shiftStart, shiftEnd,
);
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
overlay: { flex: 1, backgroundColor: 'rgba(0,0,0,0.5)', justifyContent: 'flex-end' },
sheet: {
diff --git a/src/hooks/useDynamicTheme.ts b/src/hooks/useDynamicTheme.ts
index b32b0eb..8570ed4 100644
--- a/src/hooks/useDynamicTheme.ts
+++ b/src/hooks/useDynamicTheme.ts
@@ -118,7 +118,7 @@ async function resolveTheme(): Promise {
globalCachedTheme = selected;
return selected;
} catch (err) {
- console.warn('Errore caricamento tema dinamico:', err);
+ if (__DEV__) console.warn('Errore caricamento tema dinamico:', err);
return themes.default;
} finally {
pendingFetch = null;
diff --git a/src/screens/CalendarScreen.tsx b/src/screens/CalendarScreen.tsx
index 1266625..ae72df3 100644
--- a/src/screens/CalendarScreen.tsx
+++ b/src/screens/CalendarScreen.tsx
@@ -10,7 +10,7 @@ import * as FileSystem from 'expo-file-system/legacy';
import { WebView } from 'react-native-webview';
import { MaterialIcons } from '@expo/vector-icons';
import AsyncStorage from '@react-native-async-storage/async-storage';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
import { useAirport } from '../context/AirportContext';
import { fetchAirportScheduleRaw } from '../utils/fr24api';
import {
@@ -23,7 +23,6 @@ import {
type ParsedSchedule, type ParsedEmployee, type ParsedShift,
} from '../utils/pdfShiftParser';
-const PRIMARY = '#2563EB';
const STORAGE_KEY = '@shift_import_name';
type ShiftEvent = {
@@ -198,7 +197,7 @@ export default function CalendarScreen() {
localData[iso].push({ id: e.id, title: e.title, startDate: e.startDate, endDate: e.endDate });
// Lavoro has priority over Riposo for dot color
if (e.title.includes('Lavoro') || !dots[iso]) {
- dots[iso] = e.title.includes('Riposo') ? '#10b981' : PRIMARY;
+ dots[iso] = e.title.includes('Riposo') ? '#10b981' : colors.primary;
}
}
});
@@ -206,7 +205,7 @@ export default function CalendarScreen() {
setEventsData(localData);
setLoading(false);
fetchWeatherAndFlights(start, end, localData);
- } catch (e) { console.error(e); setLoading(false); }
+ } catch (e) { if (__DEV__) console.error(e); setLoading(false); }
};
const fetchWeatherAndFlights = async (start: Date, end: Date, localData: Record) => {
@@ -224,7 +223,7 @@ export default function CalendarScreen() {
dict[date] = { weatherText: m.text, weatherIcon: m.icon, flightCount: 0 };
});
}
- } catch (e) { console.warn('[calWeather]', e); }
+ } catch (e) { if (__DEV__) console.warn('[calWeather]', e); }
try {
const { arrivals, departures } = await fetchAirportScheduleRaw(airportCode);
const allF = [...arrivals, ...departures];
@@ -240,7 +239,7 @@ export default function CalendarScreen() {
if (dict[iso]) dict[iso].flightCount = cnt; else dict[iso] = { weatherText: 'N/A', weatherIcon: '❓', flightCount: cnt };
}
});
- } catch (e) { console.warn('[calFlights]', e); }
+ } catch (e) { if (__DEV__) console.warn('[calFlights]', e); }
setDailyStats(dict);
};
@@ -270,7 +269,7 @@ export default function CalendarScreen() {
setImportModalVisible(true);
setPdfHtml(getPdfExtractorHtml(base64));
} catch (e: any) {
- console.error(`Import error at step=${step}:`, e);
+ if (__DEV__) console.error(`Import error at step=${step}:`, e);
Alert.alert('Errore', `Errore (${step}): ${e?.message || e}`);
}
};
@@ -311,7 +310,7 @@ export default function CalendarScreen() {
setImportStep('pickName');
} catch (e) {
- console.error(e);
+ if (__DEV__) console.error(e);
Alert.alert('Errore', 'Errore nel parsing del PDF');
setImportModalVisible(false);
setImportStep('idle');
@@ -357,7 +356,7 @@ export default function CalendarScreen() {
Alert.alert('Importazione completata', `${saved} turni salvati nel calendario`);
}, 800);
} catch (e) {
- console.error(e);
+ if (__DEV__) console.error(e);
Alert.alert('Errore', 'Errore durante il salvataggio');
setImportStep('idle');
}
@@ -730,7 +729,7 @@ export default function CalendarScreen() {
);
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
pageHeader: { backgroundColor: c.card, paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: c.border },
pageTitle: { fontSize: 22, fontWeight: 'bold', color: c.primaryDark },
diff --git a/src/screens/FlightScreen.tsx b/src/screens/FlightScreen.tsx
index 155fb94..cce1020 100644
--- a/src/screens/FlightScreen.tsx
+++ b/src/screens/FlightScreen.tsx
@@ -8,7 +8,7 @@ import * as Calendar from 'expo-calendar';
import * as Notifications from 'expo-notifications';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
import { useAirport } from '../context/AirportContext';
import { getAirlineOps, getAirlineColor } from '../utils/airlineOps';
import { fetchAirportScheduleRaw } from '../utils/fr24api';
@@ -34,7 +34,7 @@ try { Notifications.setNotificationHandler({
shouldShowBanner: true,
shouldShowList: true,
}),
-}); } catch (e) { console.warn('[notifHandler]', e); }
+}); } catch (e) { if (__DEV__) console.warn('[notifHandler]', e); }
function LogoPill({ iataCode, airlineName, color }: { iataCode: string; airlineName: string; color: string }) {
@@ -396,7 +396,7 @@ export default function FlightScreen() {
await cancelPreviousNotifications();
setScheduledCount(0);
}
- } catch (e) { console.error('[fetchAll]', e); } finally { setLoading(false); setRefreshing(false); }
+ } catch (e) { if (__DEV__) console.error('[fetchAll]', e); } finally { setLoading(false); setRefreshing(false); }
}, [airportCode, airportLoading]);
useEffect(() => {
@@ -461,7 +461,7 @@ export default function FlightScreen() {
const tab = activeTab;
await AsyncStorage.setItem(PINNED_FLIGHT_KEY, JSON.stringify({ ...item, _pinTab: tab, _pinnedAt: Date.now() }));
setPinnedFlightId(id);
- try { await schedulePinnedNotifications(item, tab); } catch (e) { console.warn('[pinnedNotif]', e); }
+ try { await schedulePinnedNotifications(item, tab); } catch (e) { if (__DEV__) console.warn('[pinnedNotif]', e); }
// Send to watch
if (WearDataSender) {
const payload = JSON.stringify({
@@ -488,10 +488,10 @@ export default function FlightScreen() {
const unpinFlight = useCallback(async () => {
try {
await AsyncStorage.removeItem(PINNED_FLIGHT_KEY);
- try { await cancelPinnedNotifications(); } catch (e) { console.warn('[cancelPinNotif]', e); }
+ try { await cancelPinnedNotifications(); } catch (e) { if (__DEV__) console.warn('[cancelPinNotif]', e); }
setPinnedFlightId(null);
if (WearDataSender) WearDataSender.clearPinnedFlight();
- } catch (e) { console.error('[unpin]', e); }
+ } catch (e) { if (__DEV__) console.error('[unpin]', e); }
}, []);
const userShift = activeDay === 'today' ? shifts.today : shifts.tomorrow;
@@ -726,7 +726,7 @@ export default function FlightScreen() {
);
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
pageHeader: { backgroundColor: c.card, paddingHorizontal: 16, paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: c.border, flexDirection: 'row', alignItems: 'center' },
notifBtn: { width: 42, height: 42, borderRadius: 21, backgroundColor: c.cardSecondary, justifyContent: 'center', alignItems: 'center' },
diff --git a/src/screens/HomeScreen.tsx b/src/screens/HomeScreen.tsx
index 466c5ef..696690a 100644
--- a/src/screens/HomeScreen.tsx
+++ b/src/screens/HomeScreen.tsx
@@ -10,7 +10,7 @@ import { WebView } from 'react-native-webview';
import * as ImagePicker from 'expo-image-picker';
import * as Calendar from 'expo-calendar';
import * as Location from 'expo-location';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
import ShiftTimeline from '../components/ShiftTimeline';
import { getAirlineOps, getAirlineColor } from '../utils/airlineOps';
@@ -253,7 +253,7 @@ export default function HomeScreen() {
const events = await Calendar.getEventsAsync([cal.id], d, dEnd);
const shift = events.find(e => e.title.includes('Lavoro') || e.title.includes('Riposo'));
setShiftEvent(shift || null);
- } catch (e) { console.error('[shift]', e); } finally { setLoadingShift(false); }
+ } catch (e) { if (__DEV__) console.error('[shift]', e); } finally { setLoadingShift(false); }
};
const fetchWeather = async () => {
@@ -267,7 +267,7 @@ export default function HomeScreen() {
const temp = Math.round(json.current?.temperature_2m ?? 0);
const w = weatherMap[code] || { text: 'Sereno', icon: '☀️' };
setWeather({ ...w, temp });
- } catch (e) { console.warn('[weather]', e); }
+ } catch (e) { if (__DEV__) console.warn('[weather]', e); }
};
const pickImage = async () => {
@@ -291,7 +291,7 @@ export default function HomeScreen() {
true;
`);
}
- } catch (e) { console.error('[imagePicker]', e); setProcessing(false); }
+ } catch (e) { if (__DEV__) console.error('[imagePicker]', e); setProcessing(false); }
};
const handleWebViewMessage = (event: any) => {
@@ -299,7 +299,7 @@ export default function HomeScreen() {
const r = JSON.parse(event.nativeEvent.data);
if (r.success) setOcrText(r.text);
else Alert.alert('Errore riconoscimento testo', r.error || 'Prova con un\'immagine più nitida o meglio illuminata.');
- } catch (e) { console.error('[ocrMessage]', e); } finally { setProcessing(false); }
+ } catch (e) { if (__DEV__) console.error('[ocrMessage]', e); } finally { setProcessing(false); }
};
const parseAndSave = async () => {
@@ -429,7 +429,7 @@ export default function HomeScreen() {
);
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
hiddenWV: { height: 1, width: 1, opacity: 0, position: 'absolute', top: -100 },
topRow: { flexDirection: 'row', gap: 12, padding: 16, paddingBottom: 8 },
diff --git a/src/screens/ManualsScreen.tsx b/src/screens/ManualsScreen.tsx
index f20e5b3..ad9d783 100644
--- a/src/screens/ManualsScreen.tsx
+++ b/src/screens/ManualsScreen.tsx
@@ -5,7 +5,7 @@ import {
LayoutAnimation, Platform, UIManager, TextInput, Modal, Alert, KeyboardAvoidingView,
} from 'react-native';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
const STORAGE_KEY = 'manuals_data_v2';
@@ -416,7 +416,7 @@ function CommandsTab({ commands, colors }: { commands: DCSCommand[]; colors: any
}
// ─── Item component ───────────────────────────────────────────────────────────
-function makeItemStyles(c: any) {
+function makeItemStyles(c: ThemeColors) {
return StyleSheet.create({
wrapper: {
backgroundColor: c.card,
@@ -485,7 +485,7 @@ function ManualItemRow({
}
// ─── Section component ────────────────────────────────────────────────────────
-function makeSectionStyles(c: any) {
+function makeSectionStyles(c: ThemeColors) {
return StyleSheet.create({
wrapper: {
marginBottom: 12,
@@ -583,7 +583,7 @@ const modalStyles = StyleSheet.create({
});
// ─── Main Screen ──────────────────────────────────────────────────────────────
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
root: { flex: 1, backgroundColor: c.bg },
header: {
diff --git a/src/screens/NotepadScreen.tsx b/src/screens/NotepadScreen.tsx
index 4a71441..158ee78 100644
--- a/src/screens/NotepadScreen.tsx
+++ b/src/screens/NotepadScreen.tsx
@@ -5,11 +5,11 @@ import {
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
const STORAGE_KEY = 'aerostaff_notepad_v1';
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
root: { flex: 1, backgroundColor: c.bg },
toolbar: {
@@ -27,7 +27,7 @@ function makeStyles(c: any) {
backgroundColor: c.primary, borderRadius: 10,
paddingHorizontal: 14, paddingVertical: 8,
},
- saveBtnDim: { backgroundColor: '#93C5FD' },
+ saveBtnDim: { opacity: 0.55 },
saveTxt: { color: '#fff', fontWeight: '600', fontSize: 13 },
statusBar: {
flexDirection: 'row', alignItems: 'center', gap: 6,
diff --git a/src/screens/PasswordScreen.tsx b/src/screens/PasswordScreen.tsx
index 54ed239..cef8710 100644
--- a/src/screens/PasswordScreen.tsx
+++ b/src/screens/PasswordScreen.tsx
@@ -6,7 +6,7 @@ import {
import AsyncStorage from '@react-native-async-storage/async-storage';
import * as SecureStore from 'expo-secure-store';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
const PASSWORDS_KEY = 'aerostaff_passwords_v1';
const PIN_KEY = 'aerostaff_pin_v1';
@@ -167,7 +167,7 @@ export default function PasswordScreen() {
setPinEnabled(false);
await AsyncStorage.setItem(PIN_ENABLED_KEY, 'false');
await deleteSecurePin();
- } catch (e) { console.error('[pin] disable error', e); }
+ } catch (e) { if (__DEV__) console.error('[pin] disable error', e); }
}},
]);
} else {
@@ -183,7 +183,7 @@ export default function PasswordScreen() {
setPinMode(null);
Alert.alert('PIN impostato', 'La schermata password è ora protetta.');
} catch (e) {
- console.error('[pin] setup error', e);
+ if (__DEV__) console.error('[pin] setup error', e);
Alert.alert('Errore', 'Impossibile impostare il PIN. Riprova.');
}
}, []);
@@ -197,7 +197,7 @@ export default function PasswordScreen() {
Alert.alert('PIN errato', 'Riprova.');
}
} catch (e) {
- console.error('[pin] unlock error', e);
+ if (__DEV__) console.error('[pin] unlock error', e);
Alert.alert('Errore', 'Impossibile verificare il PIN. Riprova.');
}
}, []);
@@ -349,7 +349,7 @@ export default function PasswordScreen() {
}
// ─── Styles ───────────────────────────────────────────────────────────────────
-function makePinStyles(c: any) {
+function makePinStyles(c: ThemeColors) {
return StyleSheet.create({
overlay: { flex: 1, backgroundColor: c.bg, justifyContent: 'center', alignItems: 'center' },
box: { alignItems: 'center', padding: 32, width: '100%', maxWidth: 320 },
@@ -364,7 +364,7 @@ function makePinStyles(c: any) {
});
}
-function makeRowStyles(c: any) {
+function makeRowStyles(c: ThemeColors) {
return StyleSheet.create({
card: { backgroundColor: c.card, borderRadius: 14, padding: 14, marginBottom: 10, flexDirection: 'row', alignItems: 'flex-start', borderWidth: 1, borderColor: c.border, shadowColor: '#000', shadowOpacity: c.isDark ? 0 : 0.05, shadowRadius: 6, elevation: c.isDark ? 0 : 2 },
cardLeft:{ flex: 1 },
@@ -380,7 +380,7 @@ function makeRowStyles(c: any) {
});
}
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
root: { flex: 1, backgroundColor: c.bg },
toolbar: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingHorizontal: 16, paddingVertical: 12, backgroundColor: c.card, borderBottomWidth: 1, borderBottomColor: c.border },
diff --git a/src/screens/PhonebookScreen.tsx b/src/screens/PhonebookScreen.tsx
index 5f2cf27..5770032 100644
--- a/src/screens/PhonebookScreen.tsx
+++ b/src/screens/PhonebookScreen.tsx
@@ -5,7 +5,7 @@ import {
} from 'react-native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { MaterialIcons } from '@expo/vector-icons';
-import { useAppTheme } from '../context/ThemeContext';
+import { useAppTheme, type ThemeColors } from '../context/ThemeContext';
const STORAGE_KEY = 'aerostaff_phonebook_v1';
@@ -40,7 +40,7 @@ interface EditModalProps {
onClose: () => void;
}
-function makeModalStyles(c: any) {
+function makeModalStyles(c: ThemeColors) {
return StyleSheet.create({
overlay: {
flex: 1, justifyContent: 'flex-end',
@@ -205,7 +205,7 @@ function EditModal({ visible, contact, onSave, onClose }: EditModalProps) {
// ─── Riga contatto ────────────────────────────────────────────────────────────
-function makeRowStyles(c: any) {
+function makeRowStyles(c: ThemeColors) {
return StyleSheet.create({
card: {
flexDirection: 'row', alignItems: 'center',
@@ -285,7 +285,7 @@ function ContactRow({ contact, onEdit, onDelete }: ContactRowProps) {
// ─── Main Screen ──────────────────────────────────────────────────────────────
-function makeStyles(c: any) {
+function makeStyles(c: ThemeColors) {
return StyleSheet.create({
root: { flex: 1, backgroundColor: c.bg },
header: {
diff --git a/src/utils/autoNotifications.ts b/src/utils/autoNotifications.ts
index 4c44518..3f0e7f2 100644
--- a/src/utils/autoNotifications.ts
+++ b/src/utils/autoNotifications.ts
@@ -93,7 +93,7 @@ export async function autoScheduleNotifications(): Promise {
});
newIds.push(id);
} catch (err) {
- console.error('Failed to schedule arrival notification:', err);
+ if (__DEV__) console.error('Failed to schedule arrival notification:', err);
}
}
@@ -146,7 +146,7 @@ export async function autoScheduleNotifications(): Promise {
newIds.push(id);
}
} catch (err) {
- console.error('Failed to schedule departure notification:', err);
+ if (__DEV__) console.error('Failed to schedule departure notification:', err);
}
}
@@ -170,7 +170,7 @@ export async function autoScheduleNotifications(): Promise {
await AsyncStorage.setItem(LAST_SCHEDULE_KEY, todayKey);
return newIds.length;
} catch (e) {
- console.error('autoScheduleNotifications error:', e);
+ if (__DEV__) console.error('autoScheduleNotifications error:', e);
return 0;
}
}
From 7e0cf9b638b5ff5c93af25ff28d0b658d7852412 Mon Sep 17 00:00:00 2001
From: TargetMisser <52361977+TargetMisser@users.noreply.github.com>
Date: Mon, 6 Apr 2026 17:19:48 +0200
Subject: [PATCH 2/2] fix: address code review blockers
- DrawerMenu: import version from package.json instead of hardcoded string
- eliminates version drift between package.json and UI
- requires resolveJsonModule (included in expo/tsconfig.base)
- NotepadScreen: add comment explaining intentional opacity cascade on saveBtnDim
- opacity: 0.55 dims entire button (icon + label + bg) for 'already saved' state
- intentional UX: full-button fade signals inactive state
- Add src/utils/devLog.ts utility (devLog/devWarn/devError)
- centralises __DEV__ guards for future use
- Metro eliminates dead branches in production bundles
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
src/components/DrawerMenu.tsx | 3 ++-
src/screens/NotepadScreen.tsx | 2 ++
src/utils/devLog.ts | 20 ++++++++++++++++++++
3 files changed, 24 insertions(+), 1 deletion(-)
create mode 100644 src/utils/devLog.ts
diff --git a/src/components/DrawerMenu.tsx b/src/components/DrawerMenu.tsx
index ebb6384..ef577d6 100644
--- a/src/components/DrawerMenu.tsx
+++ b/src/components/DrawerMenu.tsx
@@ -1,3 +1,4 @@
+import { version } from '../../package.json';
import React, { useEffect, useRef, useState, useMemo } from 'react';
import {
Animated, Modal, StyleSheet, Text, TouchableOpacity, View,
@@ -103,7 +104,7 @@ export default function DrawerMenu({ visible, onClose, onSelect }: Props) {
{/* Divider */}
- AeroStaff Pro · v1.1.0
+ AeroStaff Pro · v{version}
diff --git a/src/screens/NotepadScreen.tsx b/src/screens/NotepadScreen.tsx
index 158ee78..f33ea00 100644
--- a/src/screens/NotepadScreen.tsx
+++ b/src/screens/NotepadScreen.tsx
@@ -27,6 +27,8 @@ function makeStyles(c: ThemeColors) {
backgroundColor: c.primary, borderRadius: 10,
paddingHorizontal: 14, paddingVertical: 8,
},
+ // Dims the entire save button (background + icon + label) when content is
+ // already saved — intentional: the full-button fade signals an inactive state.
saveBtnDim: { opacity: 0.55 },
saveTxt: { color: '#fff', fontWeight: '600', fontSize: 13 },
statusBar: {
diff --git a/src/utils/devLog.ts b/src/utils/devLog.ts
new file mode 100644
index 0000000..8ae1dae
--- /dev/null
+++ b/src/utils/devLog.ts
@@ -0,0 +1,20 @@
+/**
+ * Development-only logging helpers.
+ * In production builds Metro eliminates the dead `if (false)` branches,
+ * so these calls compile away entirely — no performance cost in prod.
+ */
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const devWarn = (...args: any[]): void => {
+ if (__DEV__) console.warn(...args);
+};
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const devError = (...args: any[]): void => {
+ if (__DEV__) console.error(...args);
+};
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+export const devLog = (...args: any[]): void => {
+ if (__DEV__) console.log(...args);
+};