Skip to content

Commit 9936401

Browse files
authored
Merge pull request #24 from badgerloop-software/fullscreen-dashboard-update
Fullscreen dashboard update
2 parents a4f8b99 + aac310b commit 9936401

283 files changed

Lines changed: 7396 additions & 34245 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/deploy-pages.yml

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@
1010
# - EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY (if the app needs it at build time). Use a production Clerk key for Pages.
1111
# - MAPBOX_ACCESS_TOKEN (optional; also in app.config extra).
1212
# - METEOBLUE_API_KEY (optional; Meteoblue map tiles on Strategy — must be a plain string secret).
13-
# Classic dashboard WebSocket: on HTTPS, ws:// is blocked. To stream from a bridge on Pages builds,
14-
# add a workflow step that builds classic-dashboard with REACT_APP_CLASSIC_WS_BASE=wss://... (secret).
1513
# 3. Clerk Dashboard → Native applications → allowlist SSO redirect URLs, e.g.
1614
# https://<owner>.github.io/<repo>/oauth-native-callback
1715
# https://<owner>.github.io/<repo>/ (after sign-out)
@@ -65,13 +63,6 @@ jobs:
6563
- name: Install dependencies
6664
run: npm ci
6765

68-
# CRA uses relative ./static/* ; without a trailing slash, /repo/classic-dashboard resolves
69-
# ./static to /repo/static (wrong). <base href> fixes direct links to .../classic-dashboard
70-
- name: Classic dashboard base URL for Pages
71-
env:
72-
CLASSIC_BASE_HREF: /${{ github.event.repository.name }}/classic-dashboard
73-
run: node scripts/inject-classic-dashboard-base.cjs
74-
7566
- name: Export web (static)
7667
env:
7768
EXPO_PUBLIC_BASE_PATH: /${{ github.event.repository.name }}

README.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,5 +106,3 @@ sc2-mobile-app/
106106
### License / Model credit:
107107
This work uses the Ferrari F40 model by Black Snow on Sketchfab, licensed under CC-BY-4.0. See src/car-visualizer/ferrari_f40/license.txt for details.
108108
109-
### Classic (chase car) dashboard on web
110-
The Classic View dashboard UI is derived from the [chase-car-dashboard](https://github.com/badgerloop-software/chase-car-dashboard) **Frontend** subtree; upstream authorship is preserved in git. See [docs/chase-car-dashboard-git-subtree.md](docs/chase-car-dashboard-git-subtree.md) for merge details and how to refresh the static build under `public/classic-dashboard/`.

app/(tabs)/_layout.js

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { useAuthGuard } from '../../src/hooks/useAuthGuard';
99
import useDeviceType from '../../src/hooks/useDeviceType';
1010
import { useNavigation } from '../../src/context/NavigationContext';
1111
import { useUser } from '@clerk/expo';
12+
import { useTelemetryUi } from '../../src/context/TelemetryUiContext';
1213
import { getExpoWebBasePath } from '../../src/lib/expoWebBasePath';
1314

1415
/** Href segment for Expo Router tab links (with optional GitHub Pages `experiments.baseUrl`). */
@@ -21,6 +22,7 @@ function tabHref(pathFromRoot) {
2122

2223
export default function TabLayout() {
2324
const { isDark } = useTheme();
25+
const { openTelemetryQuickSettings } = useTelemetryUi();
2426
const { isLoaded } = useAuthGuard();
2527
const { user } = useUser();
2628
const deviceType = useDeviceType();
@@ -32,7 +34,7 @@ export default function TabLayout() {
3234
const isWebInfotainment = Platform.OS === 'web' && deviceType.isInfotainmentMode;
3335
const isWebInfotainmentWithMap = isWebInfotainment && showInfotainmentMap;
3436

35-
// Classic dashboard + Account tab: large / infotainment only. Settings stays on the bar for phones & narrow web.
37+
// Fullscreen dashboard + Account tab: large / infotainment only. Settings stays on the bar for phones & narrow web.
3638
const showInfotainmentExtraTabs = deviceType.isInfotainmentMode;
3739

3840
// Web uses standard Expo Router Tabs to avoid NativeTabs icon rasterization errors.
@@ -52,10 +54,11 @@ export default function TabLayout() {
5254
: `[role="tablist"] a[href="/"]::after`;
5355
const tabRules = [
5456
`${homeSelectors} { content: "Home"; }`,
57+
`[role="tablist"] a[href="${tabHref('/fullscreen-dashboard')}"]::after { content: "Dashboard"; }`,
5558
`[role="tablist"] a[href="${tabHref('/systems')}"]::after { content: "Systems"; }`,
56-
`[role="tablist"] a[href="${tabHref('/classic-dashboard')}"]::after { content: "Classic"; }`,
5759
`[role="tablist"] a[href="${tabHref('/signal-search')}"]::after { content: "Signals"; }`,
5860
`[role="tablist"] a[href="${tabHref('/account')}"]::after { content: "Account"; }`,
61+
`[role="tablist"] a[href="${tabHref('/telemetry-source')}"]::after { content: "Telemetry source"; }`,
5962
`[role="tablist"] a[href="${tabHref('/profile')}"]::after { content: "Settings"; }`,
6063
].join('\n ');
6164
style.textContent = `
@@ -163,14 +166,15 @@ export default function TabLayout() {
163166
}, [isDark]);
164167

165168
return (
169+
<View style={{ flex: 1 }}>
166170
<Tabs
167171
screenOptions={{
168172
headerShown: false,
169173
sceneStyle: isDesktopSidebar
170174
? {
171175
paddingBottom: 0,
172176
marginBottom: 0,
173-
// Tab bar is position absolute, width 56 — inset scenes so content (e.g. Classic iframe) is not covered
177+
// Tab bar is position absolute, width 56 — inset scenes so content is not covered
174178
paddingLeft: 56,
175179
}
176180
: undefined,
@@ -238,11 +242,11 @@ export default function TabLayout() {
238242
}}
239243
/>
240244
<Tabs.Screen
241-
name="classic-dashboard"
245+
name="fullscreen-dashboard"
242246
options={{
243-
title: 'Classic',
247+
title: 'Dashboard',
244248
href: showInfotainmentExtraTabs ? undefined : null,
245-
tabBarIcon: ({ color, size }) => <Ionicons name="speedometer-outline" size={isDesktopSidebar ? 22 : size} color={color} />,
249+
tabBarIcon: ({ color, size }) => <Ionicons name="grid-outline" size={isDesktopSidebar ? 22 : size} color={color} />,
246250
}}
247251
/>
248252
<Tabs.Screen
@@ -294,6 +298,22 @@ export default function TabLayout() {
294298
),
295299
}}
296300
/>
301+
<Tabs.Screen
302+
name="telemetry-source"
303+
listeners={{
304+
tabPress: (e) => {
305+
e?.preventDefault?.();
306+
openTelemetryQuickSettings();
307+
},
308+
}}
309+
options={{
310+
title: 'Telemetry source',
311+
href: showInfotainmentExtraTabs ? undefined : null,
312+
tabBarIcon: ({ color, size }) => (
313+
<Ionicons name="pulse" size={isDesktopSidebar ? 22 : size} color={color} />
314+
),
315+
}}
316+
/>
297317
<Tabs.Screen
298318
name="profile"
299319
options={{
@@ -302,6 +322,7 @@ export default function TabLayout() {
302322
}}
303323
/>
304324
</Tabs>
325+
</View>
305326
);
306327
}
307328

@@ -333,11 +354,11 @@ export default function TabLayout() {
333354
})}
334355
</NativeTabs.Trigger>
335356

336-
<NativeTabs.Trigger name="classic-dashboard" hidden={!showInfotainmentExtraTabs}>
337-
<Label>Classic</Label>
357+
<NativeTabs.Trigger name="fullscreen-dashboard" hidden={!showInfotainmentExtraTabs}>
358+
<Label>Dashboard</Label>
338359
{Platform.select({
339-
ios: <Icon sf={{ default: 'speedometer', selected: 'speedometer' }} />,
340-
android: <Icon src={<VectorIcon family={Ionicons} name="speedometer-outline" />} />,
360+
ios: <Icon sf={{ default: 'square.grid.2x2', selected: 'square.grid.2x2.fill' }} />,
361+
android: <Icon src={<VectorIcon family={Ionicons} name="grid-outline" />} />,
341362
})}
342363
</NativeTabs.Trigger>
343364

app/(tabs)/classic-dashboard.js

Lines changed: 0 additions & 6 deletions
This file was deleted.

app/(tabs)/fullscreen-dashboard.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import FullscreenDashboardScreen from '../../src/screens/FullscreenDashboardScreen';
2+
3+
export default function FullscreenDashboardTab() {
4+
return <FullscreenDashboardScreen />;
5+
}

app/(tabs)/telemetry-source.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { View } from 'react-native';
2+
3+
/**
4+
* Route exists for the desktop sidebar tab only. The tab opens quick telemetry settings
5+
* via a tabPress listener in _layout.js (navigation is prevented).
6+
*/
7+
export default function TelemetrySourceTabScreen() {
8+
return <View style={{ flex: 1 }} />;
9+
}

app/_layout.js

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ import { Slot, useRouter, useSegments } from 'expo-router';
22
import { ThemeProvider } from '../src/theme/ThemeProvider';
33
import { NavigationProvider } from '../src/context/NavigationContext';
44
import { TelemetryProvider } from '../src/context/TelemetryContext';
5+
import { TelemetryUiProvider } from '../src/context/TelemetryUiContext';
56
import { useFonts } from 'expo-font';
67
import { useEffect, useMemo } from 'react';
78
import * as SplashScreen from 'expo-splash-screen';
89
import { GestureHandlerRootView } from 'react-native-gesture-handler';
910
import { SafeAreaProvider } from 'react-native-safe-area-context';
1011
import { StyleSheet, View, Platform } from 'react-native';
1112
import SharedMapContainer from '../src/components/SharedMapContainer';
12-
import { ClerkProvider, ClerkLoaded, useUser } from '@clerk/expo';
13+
import { ClerkProvider, ClerkLoaded } from '@clerk/expo';
1314
import * as SecureStore from 'expo-secure-store';
1415
import { createClerkExpoWebRouter, getClerkWebProviderUrls } from '../src/lib/clerkWebProviderUrls';
1516

@@ -68,7 +69,9 @@ export default function RootLayout() {
6869
<ThemeProvider>
6970
<NavigationProvider>
7071
<TelemetryProvider>
71-
<AppContent />
72+
<TelemetryUiProvider>
73+
<AppContent />
74+
</TelemetryUiProvider>
7275
</TelemetryProvider>
7376
</NavigationProvider>
7477
</ThemeProvider>
@@ -80,7 +83,6 @@ export default function RootLayout() {
8083
}
8184

8285
function AppContent() {
83-
const { user } = useUser();
8486
const segments = useSegments();
8587
const currentTopSegment = segments[0];
8688
const isAuthFlowRoute =
@@ -89,29 +91,6 @@ function AppContent() {
8991
currentTopSegment === 'sso-callback' ||
9092
currentTopSegment === 'oauth-native-callback';
9193

92-
// Keep classic iframe UI in sync with current auth status.
93-
useEffect(() => {
94-
if (Platform.OS !== 'web') return;
95-
try {
96-
const payload = user
97-
? {
98-
isSignedIn: true,
99-
name: user.fullName || user.firstName || 'Signed In User',
100-
email: user.primaryEmailAddress?.emailAddress || '',
101-
imageUrl: user.imageUrl || '',
102-
}
103-
: {
104-
isSignedIn: false,
105-
name: '',
106-
email: '',
107-
imageUrl: '',
108-
};
109-
window.localStorage.setItem('bsrClassicAuth', JSON.stringify(payload));
110-
} catch (e) {
111-
// Best-effort sync only.
112-
}
113-
}, [user]);
114-
11594
return (
11695
<View style={styles.container}>
11796
<Slot />

app/account-settings.js

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -139,20 +139,22 @@ export default function AccountSettingsScreen() {
139139
) : null}
140140
</View>
141141

142-
<View style={[styles.appSettingsCard, { backgroundColor: theme.colors.card, borderColor: theme.colors.border }]}>
143-
<Text style={[styles.sectionTitle, { color: theme.colors.text }]}>App</Text>
144-
<Text style={[styles.subtitle, { color: theme.colors.muted, marginBottom: 10 }]}>
145-
Quick link to the Settings tab (telemetry, theme, units). On phones the Account tab is hidden; open Account Settings from Settings → Account Settings.
146-
</Text>
147-
<TouchableOpacity
148-
onPress={() => router.push('/profile')}
149-
style={[styles.appSettingsBtn, { borderColor: theme.colors.border, backgroundColor: theme.colors.background }]}
150-
>
151-
<Ionicons name="settings-outline" size={18} color={theme.colors.primary} />
152-
<Text style={[styles.appSettingsBtnText, { color: theme.colors.text }]}>Open Settings tab</Text>
153-
<Ionicons name="chevron-forward" size={18} color={theme.colors.muted} style={styles.appSettingsChevron} />
154-
</TouchableOpacity>
155-
</View>
142+
{Platform.OS !== 'web' ? (
143+
<View style={[styles.appSettingsCard, { backgroundColor: theme.colors.card, borderColor: theme.colors.border }]}>
144+
<Text style={[styles.sectionTitle, { color: theme.colors.text }]}>App</Text>
145+
<Text style={[styles.subtitle, { color: theme.colors.muted, marginBottom: 10 }]}>
146+
Quick link to the Settings tab (telemetry, theme, units). On phones the Account tab is hidden; open Account Settings from Settings → Account Settings.
147+
</Text>
148+
<TouchableOpacity
149+
onPress={() => router.push('/profile')}
150+
style={[styles.appSettingsBtn, { borderColor: theme.colors.border, backgroundColor: theme.colors.background }]}
151+
>
152+
<Ionicons name="settings-outline" size={18} color={theme.colors.primary} />
153+
<Text style={[styles.appSettingsBtnText, { color: theme.colors.text }]}>Open Settings tab</Text>
154+
<Ionicons name="chevron-forward" size={18} color={theme.colors.muted} style={styles.appSettingsChevron} />
155+
</TouchableOpacity>
156+
</View>
157+
) : null}
156158

157159
{!user ? (
158160
<View style={[styles.card, { backgroundColor: theme.colors.card }]}>

classic-dashboard/.babelrc

Lines changed: 0 additions & 13 deletions
This file was deleted.

classic-dashboard/build/asset-manifest.json

Lines changed: 0 additions & 56 deletions
This file was deleted.

0 commit comments

Comments
 (0)