feat: seasonal race palette (Giro/Tour/Vuelta) + mobile picker

- theme.ts: useTheme() hook with race calendar (May–Sep windows),
  auto-detects Giro/Tour/Vuelta by date; stores override in SQLite
- All screens (feed, import, activity, tab bar) now use accent/dim
  from useTheme() instead of hardcoded #60a5fa
- Settings: Palette section with Auto/Default/Giro/Tour/Vuelta buttons
  to override the auto-detected palette for testing
This commit is contained in:
Davide Scaini
2026-04-25 15:41:20 +02:00
parent 5330b7b489
commit dfe5307ab4
6 changed files with 111 additions and 50 deletions
+11 -9
View File
@@ -6,6 +6,7 @@ import { ActivityIndicator, Alert, Modal, Pressable, ScrollView, StyleSheet, Tex
import Svg, { Defs, LinearGradient, Path, Stop } from 'react-native-svg';
import { useSQLiteContext } from 'expo-sqlite';
import { deleteActivity, useActivity, useSetting } from '@/db/queries';
import { useTheme } from '@/theme';
const MAP_STYLE = 'https://basemaps.cartocdn.com/gl/dark-matter-gl-style/style.json';
@@ -28,6 +29,7 @@ export default function ActivityScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const db = useSQLiteContext();
const theme = useTheme();
const row = useActivity(id);
const instanceUrl = useSetting('instance_url')?.replace(/\/$/, '') ?? '';
const token = useSetting('api_token') ?? '';
@@ -116,7 +118,7 @@ export default function ActivityScreen() {
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<View style={styles.topBar}>
<Pressable style={styles.backButton} onPress={() => router.back()}>
<Text style={styles.backText}> Back</Text>
<Text style={[styles.backText, { color: theme.accent }]}> Back</Text>
</Pressable>
<Pressable style={styles.deleteButton} onPress={confirmDelete}>
<Text style={styles.deleteText}>Delete</Text>
@@ -128,7 +130,7 @@ export default function ActivityScreen() {
<Text style={styles.date}>{date}</Text>
{/* Map */}
<RouteMap geojson={geojson} loading={loadingMap} />
<RouteMap geojson={geojson} loading={loadingMap} accent={theme.accent} />
{/* Stats grid */}
<View style={styles.grid}>
@@ -142,7 +144,7 @@ export default function ActivityScreen() {
</View>
{/* Metric charts */}
<MetricCharts timeseries={timeseries} loading={loadingChart} />
<MetricCharts timeseries={timeseries} loading={loadingChart} accent={theme.accent} />
{/* Meta */}
<View style={styles.meta}>
@@ -157,13 +159,13 @@ export default function ActivityScreen() {
// ── Map ───────────────────────────────────────────────────────────────────────
function RouteMap({ geojson, loading }: { geojson: object | null; loading: boolean }) {
function RouteMap({ geojson, loading, accent }: { geojson: object | null; loading: boolean; accent: string }) {
const [fullscreen, setFullscreen] = useState(false);
if (loading) {
return (
<View style={styles.mapPlaceholder}>
<ActivityIndicator color="#60a5fa" />
<ActivityIndicator color={accent} />
</View>
);
}
@@ -175,7 +177,7 @@ function RouteMap({ geojson, loading }: { geojson: object | null; loading: boole
<Layer
type="line"
id="route-line"
paint={{ 'line-color': '#60a5fa', 'line-width': 3 }}
paint={{ 'line-color': accent, 'line-width': 3 }}
layout={{ 'line-cap': 'round', 'line-join': 'round' }}
/>
</GeoJSONSource>
@@ -237,13 +239,13 @@ const TAB_META: Record<TabKey, { label: string; unit: string; color: string; dec
power: { label: 'Power', unit: 'W', color: '#facc15', decimals: 0 },
};
function MetricCharts({ timeseries, loading }: { timeseries: Timeseries | null; loading: boolean }) {
function MetricCharts({ timeseries, loading, accent }: { timeseries: Timeseries | null; loading: boolean; accent: string }) {
const [activeTab, setActiveTab] = useState<TabKey>('elevation');
if (loading) {
return (
<View style={styles.chartPlaceholder}>
<ActivityIndicator color="#60a5fa" />
<ActivityIndicator color={accent} />
</View>
);
}
@@ -398,7 +400,7 @@ const styles = StyleSheet.create({
notFound: { color: '#71717a', fontSize: 16 },
topBar: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingTop: 60, paddingBottom: 12 },
backButton: { paddingHorizontal: 16 },
backText: { color: '#60a5fa', fontSize: 15 },
backText: { fontSize: 15 },
deleteButton: { paddingHorizontal: 16 },
deleteText: { color: '#f87171', fontSize: 15 },
sport: { color: '#71717a', fontSize: 12, fontWeight: '600', letterSpacing: 0.8, paddingHorizontal: 16, marginBottom: 4 },