import React, { useCallback, useMemo, useState } from 'react';
import {
StatusBar,
StyleSheet,
Text,
TouchableOpacity,
View,
} from 'react-native';
import { GestureHandlerRootView } from 'react-native-gesture-handler';
import { callback } from 'react-native-nitro-modules';
import type { CameraState, CesiumMetrics } from 'react-native-cesium';
import { CesiumView } from 'react-native-cesium';
import {
SafeAreaProvider,
useSafeAreaInsets,
} from 'react-native-safe-area-context';
import { CESIUM_ION_ACCESS_TOKEN, ION_ACCESS_TOKEN } from '@env';
import { Joystick } from './components/Joystick';
import { LayerPicker } from './components/LayerPicker';
import { CreditsDialog } from './components/CreditsDialog';
import { MapGestureHandler } from './components/MapGestureHandler';
import { useCameraController } from './hooks/useCameraController';
const ionAccessToken = (CESIUM_ION_ACCESS_TOKEN ?? ION_ACCESS_TOKEN ?? '').trim();
const hasIonToken = ionAccessToken.length > 0;
const INITIAL_CAMERA: CameraState = {
latitude: 46.02,
longitude: 7.6,
altitude: 5800,
heading: 220,
pitch: -20,
roll: 0,
verticalFovDeg: 60,
};
function AppContent() {
const insets = useSafeAreaInsets();
const [imageryAssetId, setImageryAssetId] = useState(1);
const [credits, setCredits] = useState('');
const [creditsExpanded, setCreditsExpanded] = useState(false);
const [tilesLoading, setTilesLoading] = useState(0);
const [tilesRendered, setTilesRendered] = useState(0);
const [fps, setFps] = useState(0);
const { camera, hudCamera, setCesiumView, handleJoystickRates } =
useCameraController(INITIAL_CAMERA);
const hybridRef = useMemo(() => callback(setCesiumView), [setCesiumView]);
const handleMetrics = useCallback((m: CesiumMetrics) => {
setCredits((prev) => (prev === m.creditsPlainText ? prev : m.creditsPlainText));
setTilesLoading(Math.round(m.tilesLoading));
setTilesRendered(Math.round(m.tilesRendered));
setFps(Math.round(m.fps));
}, []);
const metricsCallback = useMemo(
() => callback(handleMetrics),
[handleMetrics],
);
const handleCloseCredits = useCallback(() => setCreditsExpanded(false), []);
const handleOpenCredits = useCallback(() => setCreditsExpanded(true), []);
return (
{!hasIonToken && (
Cesium Ion token missing
Create `example/.env` from `example/.env_example` and set
`CESIUM_ION_ACCESS_TOKEN`.
)}
{hasIonToken && (
Lat {hudCamera.latitude.toFixed(5)}°, Lon{' '}
{hudCamera.longitude.toFixed(5)}°
Alt {Math.round(hudCamera.altitude).toLocaleString()} m
Heading {hudCamera.heading.toFixed(1)}°
Tiles {tilesRendered} rendered · {tilesLoading} loading · {fps} fps
)}
0 ? 24 : 0) },
]}
>
Pitch / Roll
{credits.length > 0 && (
ⓘ {credits}
)}
);
}
function App() {
return (
);
}
const styles = StyleSheet.create({
rootFill: { flex: 1 },
container: { flex: 1, backgroundColor: '#000' },
bottomCenter: {
position: 'absolute',
alignSelf: 'center',
alignItems: 'center',
gap: 10,
},
joystickLabel: {
color: 'rgba(255,255,255,0.55)',
fontSize: 10,
fontWeight: '500',
letterSpacing: 0.5,
},
creditsBar: {
position: 'absolute',
left: 8,
right: 8,
backgroundColor: 'rgba(0,0,0,0.50)',
borderRadius: 4,
paddingHorizontal: 8,
paddingVertical: 3,
},
creditsBarText: {
textAlign: 'center',
fontSize: 10,
color: 'rgba(255,255,255,0.70)',
},
cameraHud: {
position: 'absolute',
left: 12,
backgroundColor: 'rgba(0,0,0,0.32)',
borderRadius: 8,
paddingHorizontal: 10,
paddingVertical: 8,
gap: 2,
},
cameraHudText: {
color: '#fff',
fontSize: 12,
fontVariant: ['tabular-nums'],
},
missingTokenBanner: {
position: 'absolute',
left: 12,
right: 12,
backgroundColor: 'rgba(120, 20, 20, 0.92)',
borderRadius: 10,
paddingHorizontal: 12,
paddingVertical: 10,
gap: 4,
},
missingTokenTitle: {
color: '#fff',
fontSize: 14,
fontWeight: '700',
},
missingTokenBody: {
color: 'rgba(255,255,255,0.9)',
fontSize: 12,
lineHeight: 18,
},
});
export default App;