mirror of
https://github.com/geoffsee/open-gsio.git
synced 2025-09-08 22:56:46 +00:00
- Introduce MapStore
to manage map state and controls using MobX-State-Tree.
- Integrate `MapStore` into `ClientChatStore`. - Add support for handling map control tool responses in `StreamStore`. - Update `InputMenu` with loading state while fetching models and UI improvements. - Include `useLayoutEffect` in `LandingComponent` for persistent state management. - Enhance `ChatService` with debug logs, model fallback handling, and better error reporting.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useEffect, useLayoutEffect, useState } from 'react';
|
||||
|
||||
import { useComponent } from '../contexts/ComponentContext.tsx';
|
||||
|
||||
@@ -11,6 +11,23 @@ export const LandingComponent: React.FC = () => {
|
||||
const [mapActive, setMapActive] = useState(true);
|
||||
const [aiActive, setAiActive] = useState(false);
|
||||
|
||||
const appCtlState = `app-ctl-state`;
|
||||
|
||||
useLayoutEffect(() => {
|
||||
const value = localStorage.getItem(appCtlState);
|
||||
if (value) {
|
||||
const parsed = JSON.parse(value);
|
||||
setIntensity(parsed.intensity);
|
||||
setMapActive(parsed.mapActive);
|
||||
setAiActive(parsed.aiActive);
|
||||
}
|
||||
}, []);
|
||||
|
||||
// create a hook for saving the state as a json object when it changes
|
||||
useEffect(() => {
|
||||
localStorage.setItem(appCtlState, JSON.stringify({ intensity, mapActive, aiActive }));
|
||||
});
|
||||
|
||||
const component = useComponent();
|
||||
const { setEnabledComponent } = component;
|
||||
|
||||
@@ -21,12 +38,14 @@ export const LandingComponent: React.FC = () => {
|
||||
if (aiActive) {
|
||||
setEnabledComponent('ai');
|
||||
}
|
||||
}, []);
|
||||
}, [mapActive, aiActive, setEnabledComponent]);
|
||||
|
||||
return (
|
||||
<Box as="section" bg="background.primary" overflow="hidden">
|
||||
<Box position="fixed" right={0} maxWidth="300px" minWidth="200px" zIndex={1000}>
|
||||
<Tweakbox
|
||||
id="app-tweaker"
|
||||
persist={true}
|
||||
sliders={{
|
||||
intensity: {
|
||||
value: intensity,
|
||||
@@ -68,7 +87,6 @@ export const LandingComponent: React.FC = () => {
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
{/*<BevyScene speed={speed} intensity={intensity} glow={glow} visible={bevyScene} />*/}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
@@ -37,10 +37,10 @@ const key =
|
||||
function Map(props: { visible: boolean }) {
|
||||
return (
|
||||
/* Full-screen wrapper — fills the viewport and becomes the positioning context */
|
||||
<Box position={'absolute'} top={0} w="100vw" h={'100vh'} overflow="hidden">
|
||||
<Box position={'absolute'} top={0} w="100%" h={'100vh'} overflow="hidden">
|
||||
{/* Button bar — absolutely positioned inside the wrapper */}
|
||||
|
||||
<MapNext mapboxPublicKey={atob(key)} />
|
||||
<MapNext mapboxPublicKey={atob(key)} visible={props.visible} />
|
||||
{/*<Map*/}
|
||||
{/* mapboxAccessToken={atob(key)}*/}
|
||||
{/* initialViewState={mapView}*/}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
import { Box } from '@chakra-ui/react';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import Map, {
|
||||
FullscreenControl,
|
||||
GeolocateControl,
|
||||
@@ -9,25 +10,36 @@ import Map, {
|
||||
ScaleControl,
|
||||
} from 'react-map-gl/mapbox';
|
||||
|
||||
import clientChatStore from '../../stores/ClientChatStore';
|
||||
|
||||
import PORTS from './nautical-base-data.json';
|
||||
import Pin from './pin';
|
||||
|
||||
export default function MapNext(props: any = { mapboxPublicKey: '' } as any) {
|
||||
function MapNextComponent(props: any = { mapboxPublicKey: '', visible: true } as any) {
|
||||
const [popupInfo, setPopupInfo] = useState(null);
|
||||
const [isSearchOpen, setIsSearchOpen] = useState(false);
|
||||
const [isTokenLoading, setIsTokenLoading] = useState(false);
|
||||
const [authenticated, setAuthenticated] = useState(false);
|
||||
const mapRef = useRef<any>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setAuthenticated(true);
|
||||
setIsTokenLoading(false);
|
||||
}, []);
|
||||
|
||||
const [mapView, setMapView] = useState({
|
||||
longitude: -122.4,
|
||||
latitude: 37.8,
|
||||
zoom: 14,
|
||||
});
|
||||
// Handle map resize when component becomes visible
|
||||
useEffect(() => {
|
||||
if (props.visible && mapRef.current) {
|
||||
// Small delay to ensure the container is fully visible
|
||||
const timer = setTimeout(() => {
|
||||
if (mapRef.current) {
|
||||
mapRef.current.resize();
|
||||
}
|
||||
}, 100);
|
||||
|
||||
return () => clearTimeout(timer);
|
||||
}
|
||||
}, [props.visible]);
|
||||
|
||||
const handleNavigationClick = useCallback(async () => {
|
||||
console.log('handling navigation in map');
|
||||
@@ -39,7 +51,10 @@ export default function MapNext(props: any = { mapboxPublicKey: '' } as any) {
|
||||
|
||||
const handleMapViewChange = useCallback(async (evt: any) => {
|
||||
const { longitude, latitude, zoom } = evt.viewState;
|
||||
setMapView({ longitude, latitude, zoom });
|
||||
clientChatStore.setMapView(longitude, latitude, zoom);
|
||||
// setMapView({ longitude, latitude, zoom });
|
||||
|
||||
// Update the store with the new view state
|
||||
}, []);
|
||||
|
||||
const pins = useMemo(
|
||||
@@ -98,13 +113,15 @@ Type '{ city: string; population: string; image: string; state: string; latitude
|
||||
{/* </Button>*/}
|
||||
{/*</HStack>*/}
|
||||
<Map
|
||||
ref={mapRef}
|
||||
initialViewState={{
|
||||
latitude: 40,
|
||||
longitude: -100,
|
||||
zoom: 3.5,
|
||||
bearing: 0,
|
||||
pitch: 0,
|
||||
latitude: clientChatStore.mapState.latitude,
|
||||
longitude: clientChatStore.mapState.longitude,
|
||||
zoom: clientChatStore.mapState.zoom,
|
||||
bearing: clientChatStore.mapState.bearing,
|
||||
pitch: clientChatStore.mapState.pitch,
|
||||
}}
|
||||
onMove={handleMapViewChange}
|
||||
mapStyle="mapbox://styles/geoffsee/cmd1qz39x01ga01qv5acea02y"
|
||||
attributionControl={false}
|
||||
mapboxAccessToken={props.mapboxPublicKey}
|
||||
@@ -170,3 +187,6 @@ Type '{ city: string; population: string; image: string; state: string; latitude
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const MapNext = observer(MapNextComponent);
|
||||
export default MapNext;
|
||||
|
@@ -14,7 +14,7 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { ChevronDownIcon, ChevronUpIcon } from 'lucide-react';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React, { useState } from 'react';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
interface SliderControl {
|
||||
value: number;
|
||||
@@ -34,6 +34,8 @@ interface SwitchControl {
|
||||
}
|
||||
|
||||
interface TweakboxProps {
|
||||
id: string;
|
||||
persist: boolean;
|
||||
sliders: {
|
||||
speed: SliderControl;
|
||||
intensity: SliderControl;
|
||||
@@ -44,7 +46,7 @@ interface TweakboxProps {
|
||||
} & Record<string, SwitchControl>;
|
||||
}
|
||||
|
||||
const Tweakbox = observer(({ sliders, switches }: TweakboxProps) => {
|
||||
const Tweakbox = observer(({ id, persist, sliders, switches }: TweakboxProps) => {
|
||||
const [isCollapsed, setIsCollapsed] = useState(false);
|
||||
|
||||
return (
|
||||
|
Reference in New Issue
Block a user