diff --git a/.gitignore b/.gitignore
index 5fad7bdf..5f914c5a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,8 +16,13 @@ docker-compose.yaml
!.yarn/sdks
!.yarn/versions
+# IntelliJ
+.idea/workspace.xml
+.idea/tasks.xml
+
+
# For ignoring static files
*.png
-*.jpg
+*.jpg
-.vite
\ No newline at end of file
+.vite
diff --git a/.idea/prettier.xml b/.idea/prettier.xml
new file mode 100644
index 00000000..727b8b53
--- /dev/null
+++ b/.idea/prettier.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/watcherTasks.xml b/.idea/watcherTasks.xml
new file mode 100644
index 00000000..1389c394
--- /dev/null
+++ b/.idea/watcherTasks.xml
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
deleted file mode 100644
index 77483ffd..00000000
--- a/.idea/workspace.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- "associatedIndex": 8
-}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1718136713081
-
-
- 1718136713081
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/deps/web-components b/deps/web-components
index 53dd60b3..5c409881 160000
--- a/deps/web-components
+++ b/deps/web-components
@@ -1 +1 @@
-Subproject commit 53dd60b3bcb37486e2a186f4ef5a055748c19276
+Subproject commit 5c409881df9a53c68d5e7696d45f89baac3ddf06
diff --git a/src/pages/map/map-interface/app-state/handlers/fetch.ts b/src/pages/map/map-interface/app-state/handlers/fetch.ts
index c97d4f2a..14e7288a 100644
--- a/src/pages/map/map-interface/app-state/handlers/fetch.ts
+++ b/src/pages/map/map-interface/app-state/handlers/fetch.ts
@@ -1,8 +1,8 @@
import { SETTINGS, apiV2Prefix } from "@macrostrat-web/settings";
import axios from "axios";
import { joinURL } from "~/pages/map/map-interface/utils";
-import { ColumnGeoJSONRecord } from "../reducers";
-import { UPDATE_FILTERED_COLUMNS } from "../reducers/filtered-columns";
+import { ColumnGeoJSONRecord } from "./columns";
+import { UPDATE_COLUMN_FILTERS } from "../reducers/core/types";
import { XDDSnippet } from "~/types";
export const base = apiV2Prefix;
@@ -54,7 +54,7 @@ function buildColumnQueryParams(filters) {
export async function fetchFilteredColumns(
providedFilters
-): Promise {
+): Promise {
let queryString = buildColumnQueryParams(providedFilters);
let url = `${base}/columns`;
if (Object.keys(queryString).length === 0) {
diff --git a/src/pages/map/map-interface/app-state/handlers/filters.ts b/src/pages/map/map-interface/app-state/handlers/filters.ts
index 6b659fb0..d6b76d26 100644
--- a/src/pages/map/map-interface/app-state/handlers/filters.ts
+++ b/src/pages/map/map-interface/app-state/handlers/filters.ts
@@ -22,7 +22,7 @@ export async function runFilter(filter: Filter): Promise {
return {
category: "lithology",
id: filter.name ?? filter.id,
- name: filter.name ?? filter.id,
+ name: filter.name ?? filter.id.toString(),
type: filter.type,
legend_ids: [],
};
@@ -169,11 +169,13 @@ export const fetchIntervalFilter = async (
type LithologyClassFilter = {
type: FilterType.LithologyClasses;
name: string;
+ id: number;
};
type LithologyTypeFilter = {
type: FilterType.LithologyTypes;
name: string;
+ id: number;
};
type LithologyFilter = {
@@ -285,7 +287,8 @@ async function fetchAllLithTypes(
return {
category: "lithology",
id,
- name: id,
+ // TODO: revisit name/id differences
+ name: id.toString(),
type,
legend_ids,
};
diff --git a/src/pages/map/map-interface/app-state/handlers/index.ts b/src/pages/map/map-interface/app-state/handlers/index.ts
index 72822e2e..afbccafd 100644
--- a/src/pages/map/map-interface/app-state/handlers/index.ts
+++ b/src/pages/map/map-interface/app-state/handlers/index.ts
@@ -1,7 +1,12 @@
import { push } from "@lagunovsky/redux-react-router";
import { mapPagePrefix, routerBasename } from "@macrostrat-web/settings";
import axios from "axios";
-import { AppAction, AppState } from "../reducers";
+import {
+ AppAction,
+ AppState,
+ MenuPage,
+ setInfoMarkerPosition,
+} from "../reducers";
import {
base,
fetchAllColumns,
@@ -13,29 +18,25 @@ import {
} from "./fetch";
import { runFilter } from "./filters";
-import { formatCoordForZoomLevel } from "@macrostrat/mapbox-utils";
import { LineString } from "geojson";
-import { matchPath } from "react-router";
import { currentPageForPathName, isDetailPanelRoute } from "../nav-hooks";
-import { MenuPage, setInfoMarkerPosition } from "../reducers";
import { MapLayer } from "../reducers/core";
import { getInitialStateFromHash } from "../reducers/hash-string";
-import { ColumnGeoJSONRecord, findColumnsForLocation } from "./columns";
-
-function routeForActivePage(page: MenuPage) {
- let newPathname = routerBasename;
- if (page != null) {
- newPathname += "/" + page;
- }
- return newPathname;
-}
+import {
+ ColumnGeoJSONRecord,
+ ColumnSummary,
+ ColumnProperties,
+ findColumnsForLocation,
+} from "./columns";
+import { matchPath } from "react-router";
-async function actionRunner(
+export default async function actionRunner(
state: AppState,
action: AppAction,
dispatch = null
): Promise {
const coreState = state.core;
+
switch (action.type) {
case "get-initial-map-state": {
const { pathname } = state.router.location;
@@ -50,6 +51,15 @@ async function actionRunner(
state.router.location.hash
);
+ const newState = {
+ ...state,
+ core: {
+ ...coreState1,
+ initialLoadComplete: true,
+ },
+ menu: { activePage },
+ };
+
// If we are on the column route, the column layer must be enabled
// const colMatch = matchPath(
// mapPagePrefix + "/loc/:lng/:lat/column",
@@ -62,22 +72,40 @@ async function actionRunner(
// Fill out the remainder with defaults
// We always get all columns on initial load, which might be
- // a bit unnecessary
- let allColumns: ColumnGeoJSONRecord[] | null = await fetchAllColumns();
+ // a bit unnecessary and slow.
+ //let allColumns: ColumnGeoJSONRecord[] | null = await fetchAllColumns();
+
+ fetchAllColumns().then((res) => {
+ runAsyncAction(
+ newState,
+ {
+ type: "set-all-columns",
+ columns: res,
+ },
+ dispatch
+ );
+ });
dispatch({
type: "replace-state",
- state: {
- ...state,
- core: {
- ...coreState1,
- allColumns,
- initialLoadComplete: true,
- },
- menu: { activePage },
- },
+ state: newState,
});
+ // Set info marker position if it is defined
+ if (newState.core.infoMarkerPosition != null) {
+ runAsyncAction(
+ newState,
+ {
+ type: "map-query",
+ z: state.core.mapPosition.target?.zoom ?? 7,
+ ...state.core.infoMarkerPosition,
+ map_id: null,
+ columns: null,
+ },
+ dispatch
+ );
+ }
+
// Apply all filters in parallel
const newFilters = await Promise.all(
filters.map((f) => {
@@ -102,6 +130,20 @@ async function actionRunner(
return null;
}
}
+ case "set-all-columns":
+ if (state.core.infoMarkerPosition != null) {
+ fetchColumnInfo(
+ {
+ lng: state.core.infoMarkerPosition.lng,
+ lat: state.core.infoMarkerPosition.lat,
+ columns: [],
+ },
+ action.columns,
+ state.core.columnInfo,
+ dispatch
+ );
+ }
+ return action;
case "toggle-menu": {
// Push the menu onto the history stack
let activePage = state.menu.activePage;
@@ -125,56 +167,17 @@ async function actionRunner(
dispatch
);
}
- case "set-menu-page": {
- const { pathname, hash } = state.router.location;
- if (!isDetailPanelRoute(pathname)) {
- const newPathname = routeForActivePage(action.page);
- await dispatch(push({ pathname: newPathname, hash }));
- }
- return { type: "set-menu-page", page: action.page };
- }
- case "close-infodrawer":
- // If we are showing a cross-section, we need to go there
- await dispatch(
- push({
- pathname:
- state.core.crossSectionLine == null
- ? routeForActivePage(state.menu.activePage)
- : buildCrossSectionPath(state.core.crossSectionLine),
- hash: state.router.location.hash,
- })
- );
- return action;
case "toggle-cross-section": {
- let line: GeoJSON.LineString | null = null;
+ let line: LineString | null = null;
if (state.core.crossSectionLine == null) {
line = { type: "LineString", coordinates: [] };
}
- const action = {
+ const action: AppAction = {
type: "update-cross-section",
line,
};
return actionRunner(state, action, dispatch);
}
- case "update-cross-section":
- if (state.core.crossSectionLine != null) {
- // Return to the base route
- let nextPathname = "";
- const pos = state.core.infoMarkerPosition;
- if (pos != null) {
- const z = state.core.mapPosition.target?.zoom ?? 7;
- nextPathname = buildLocationPath(pos.lng, pos.lat, z);
- } else {
- nextPathname = routeForActivePage(state.menu.activePage);
- }
- await dispatch(
- push({
- pathname: nextPathname,
- hash: state.router.location.hash,
- })
- );
- }
- return action;
case "fetch-search-query":
const { term } = action;
let CancelToken = axios.CancelToken;
@@ -218,38 +221,7 @@ async function actionRunner(
return { type: "add-filter", filter: await runFilter(action.filter) };
case "get-filtered-columns":
return await fetchFilteredColumns(coreState.filters);
- case "set-cross-section-line": {
- const { line } = action;
-
- if (state.core.infoMarkerPosition == null) {
- // If we are showing a marker, that route takes precedence
- const pathname = buildCrossSectionPath(line);
- await dispatch(push({ pathname, hash: location.hash }));
- }
-
- return { type: "did-set-cross-section-line", line };
- }
- case "map-query": {
- const { lng, lat, z } = action;
- // Check if matches column detail route
- const { pathname } = state.router.location;
-
- let newPath = buildLocationPath(lng, lat, Number(z));
- if (
- pathname.startsWith(mapPagePrefix + "/loc") &&
- pathname.endsWith("/column")
- ) {
- // If so, we want to append columns to the end of the URL
- newPath += "/column";
- }
-
- return push({
- pathname: newPath,
- hash: location.hash,
- });
- //return { ...action, type: "run-map-query" };
- }
- case "run-map-query":
+ case "map-query":
const { lng, lat, z, map_id } = action;
// Get column data from the map action if it is provided.
// This saves us from having to filter the columns more inefficiently
@@ -266,58 +238,19 @@ async function actionRunner(
lat,
cancelToken: sourceMapQuery,
});
- let mapData = await runMapQuery(
- lng,
- lat,
- z,
- map_id,
- sourceMapQuery.token
- );
-
- let { columns } = action;
- // If no columns are provided, try to find them from the active column dataset
- if (
- (columns == null || columns.length == 0) &&
- state.core.allColumns != null
- ) {
- columns = findColumnsForLocation(state.core.allColumns, {
- lng,
- lat,
- }).map((c) => c.properties);
- }
- const firstColumn = columns?.[0];
- const { columnInfo } = state.core;
- if (firstColumn != null && columnInfo?.col_id != firstColumn.col_id) {
- // Get the column units if we don't have them already
- actionRunner(
- state,
- { type: "get-column-units", column: firstColumn },
- dispatch
- ).then(dispatch);
- } else if (firstColumn == null && columnInfo != null) {
- // Clear the column info if we don't have any columns
- dispatch({ type: "clear-column-info", data: null, column: null });
- }
- coreState.infoMarkerPosition = { lng, lat };
- return {
- type: "received-map-query",
- data: mapData,
- };
- case "get-column-units":
- let CancelTokenGetColumn = axios.CancelToken;
- let sourceGetColumn = CancelTokenGetColumn.source();
- dispatch({ type: "start-column-query", cancelToken: sourceGetColumn });
+ // Run a bunch of async queries in ~parallel
+ runMapQuery(lng, lat, z, map_id, sourceMapQuery.token).then((res) => {
+ dispatch({ type: "received-map-query", data: res });
+ });
- let columnData = await runColumnQuery(
- action.column,
- sourceGetColumn.token
+ fetchColumnInfo(
+ { lng, lat, columns: action.columns },
+ state.core.allColumns,
+ state.core.columnInfo,
+ dispatch
);
- return {
- type: "received-column-query",
- data: columnData,
- column: action.column,
- };
+ return;
case "get-pbdb":
let collection_nos = action.collection_nos;
dispatch({ type: "start-pdbd-query" });
@@ -330,18 +263,56 @@ async function actionRunner(
}
}
-function buildCrossSectionPath(line: LineString) {
- const pts = line.coordinates
- .map((p) => `${p[0].toFixed(4)},${p[1].toFixed(4)}`)
- .join("/");
-
- return mapPagePrefix + "/cross-section/" + pts;
+async function runAsyncAction(
+ state: AppState,
+ action: AppAction,
+ dispatch: any
+) {
+ const res = await actionRunner(state, action, dispatch);
+ if (res != null) dispatch(res);
}
-function buildLocationPath(lng: number, lat: number, z: number) {
- const ln = formatCoordForZoomLevel(lng, Number(z));
- const lt = formatCoordForZoomLevel(lat, Number(z));
- return mapPagePrefix + `/loc/${ln}/${lt}`;
+async function getColumnUnits(column: ColumnProperties, dispatch: any) {
+ let CancelTokenGetColumn = axios.CancelToken;
+ let sourceGetColumn = CancelTokenGetColumn.source();
+ dispatch({ type: "start-column-query", cancelToken: sourceGetColumn });
+
+ let columnData = await runColumnQuery(column, sourceGetColumn.token);
+ dispatch({
+ type: "received-column-query",
+ data: columnData,
+ column: column,
+ });
}
-export default actionRunner;
+type ColumnFetchParams = {
+ lng: number;
+ lat: number;
+ columns: ColumnProperties[];
+};
+
+function fetchColumnInfo(
+ params: ColumnFetchParams,
+ allColumns: ColumnGeoJSONRecord[] | null,
+ currentColumn: ColumnSummary | null,
+ dispatch: any
+): AppAction | void {
+ const { lng, lat, columns } = params;
+ let providedColumns = columns ?? [];
+
+ if (providedColumns.length == 0) {
+ // We could also just fire off a query using a lat/lon here
+ providedColumns = findColumnsForLocation(allColumns ?? [], {
+ lng,
+ lat,
+ }).map((c) => c.properties);
+ }
+ const nextColumn = providedColumns?.[0];
+ if (nextColumn != null && currentColumn?.col_id != nextColumn.col_id) {
+ // Get the column units if we don't have them already
+ getColumnUnits(nextColumn, dispatch);
+ } else if (nextColumn == null && currentColumn != null) {
+ // Clear the column info if we don't have any columns
+ dispatch({ type: "clear-column-info" });
+ }
+}
diff --git a/src/pages/map/map-interface/app-state/handlers/pathname.ts b/src/pages/map/map-interface/app-state/handlers/pathname.ts
new file mode 100644
index 00000000..192de812
--- /dev/null
+++ b/src/pages/map/map-interface/app-state/handlers/pathname.ts
@@ -0,0 +1,64 @@
+import {
+ AppState,
+ AppAction,
+ MenuPage,
+} from "~/pages/map/map-interface/app-state";
+import { push, UpdateLocationAction } from "@lagunovsky/redux-react-router";
+import { LineString } from "geojson";
+import { mapPagePrefix, routerBasename } from "@macrostrat-web/settings";
+import { formatCoordForZoomLevel } from "@macrostrat/mapbox-utils";
+
+export function pathNameAction(
+ state: AppState
+): UpdateLocationAction<"push"> | null {
+ /** Set the pathname based on the current state. Only one of a location, cross-section line,
+ * or active page can be selected at a time.
+ * The following priority is applied:
+ * 1. If a location is selected, show that location
+ * 2. If a cross-section line is selected, set the cross-section path
+ * 3. If an active page is selected, show that page
+ */
+
+ const pos = state.core.infoMarkerPosition;
+ let nextPathname: string = state.router.location.pathname;
+ if (pos != null) {
+ const z = state.core.mapPosition.target?.zoom ?? 7;
+ nextPathname = buildLocationPath(pos.lng, pos.lat, z);
+ // TODO: we could probably assign column page based on a flag in the state
+ if (state.router.location.pathname.endsWith("/column")) {
+ nextPathname += "/column";
+ }
+ } else if (state.core.crossSectionLine != null) {
+ nextPathname = buildCrossSectionPath(state.core.crossSectionLine);
+ } else if (state.menu.activePage != null) {
+ nextPathname = routeForActivePage(state.menu.activePage);
+ } else {
+ nextPathname = routerBasename;
+ }
+ if (nextPathname == state.router.location.pathname) {
+ return null;
+ }
+ return push({ pathname: nextPathname, hash: state.router.location.hash });
+}
+
+function buildCrossSectionPath(line: LineString) {
+ const pts = line.coordinates
+ .map((p) => `${p[0].toFixed(4)},${p[1].toFixed(4)}`)
+ .join("/");
+
+ return mapPagePrefix + "/cross-section/" + pts;
+}
+
+export function buildLocationPath(lng: number, lat: number, z: number) {
+ const ln = formatCoordForZoomLevel(lng, Number(z));
+ const lt = formatCoordForZoomLevel(lat, Number(z));
+ return mapPagePrefix + `/loc/${ln}/${lt}`;
+}
+
+function routeForActivePage(page: MenuPage) {
+ let newPathname = routerBasename;
+ if (page != null) {
+ newPathname += "/" + page;
+ }
+ return newPathname;
+}
diff --git a/src/pages/map/map-interface/app-state/hooks.ts b/src/pages/map/map-interface/app-state/hooks.ts
index 86656376..639b53ae 100644
--- a/src/pages/map/map-interface/app-state/hooks.ts
+++ b/src/pages/map/map-interface/app-state/hooks.ts
@@ -13,9 +13,13 @@ function useAppActions(): (action: AppAction) => Promise {
const store = useStore();
return async (action) => {
const appState = store.getState();
- const newAction = await actionRunner(appState, action, dispatch);
- if (newAction == undefined || newAction == null) return;
- dispatch(newAction as AppAction);
+ try {
+ const newAction = await actionRunner(appState, action, dispatch);
+ if (newAction == undefined || newAction == null) return;
+ dispatch(newAction as AppAction);
+ } catch (err) {
+ console.error(err);
+ }
};
}
diff --git a/src/pages/map/map-interface/app-state/reducers/core/index.ts b/src/pages/map/map-interface/app-state/reducers/core/index.ts
index 3789d692..03800382 100644
--- a/src/pages/map/map-interface/app-state/reducers/core/index.ts
+++ b/src/pages/map/map-interface/app-state/reducers/core/index.ts
@@ -253,10 +253,6 @@ export function coreReducer(
return { ...state, allColumns: action.columns };
case "received-column-query":
- // summarize units
- if (state.allColumns == null || state.allColumns.length == 0) {
- return state;
- }
return {
...state,
fetchingColumnInfo: false,
diff --git a/src/pages/map/map-interface/app-state/reducers/core/types.ts b/src/pages/map/map-interface/app-state/reducers/core/types.ts
index 5fa7375c..119009dc 100644
--- a/src/pages/map/map-interface/app-state/reducers/core/types.ts
+++ b/src/pages/map/map-interface/app-state/reducers/core/types.ts
@@ -22,7 +22,7 @@ type ASYNC_ADD_FILTER = { type: "async-add-filter"; filter: any };
type GET_FILTERED_COLUMNS = { type: "get-filtered-columns" };
type FETCH_XDD = { type: "fetch-xdd" };
type MAP_QUERY = {
- type: "map-query" | "run-map-query";
+ type: "map-query";
z: string | number;
map_id: any;
columns: ColumnProperties[] | null | undefined;
@@ -41,7 +41,7 @@ type CLOSE_INFODRAWER = { type: "close-infodrawer" };
type TOGGLE_FILTERS = { type: "toggle-filters" };
type REMOVE_FILTER = { type: "remove-filter"; filter: any };
-type UPDATE_COLUMN_FILTERS = {
+export type UPDATE_COLUMN_FILTERS = {
type: "update-column-filters";
columns: ColumnGeoJSONRecord[];
};
diff --git a/src/pages/map/map-interface/app-state/reducers/index.ts b/src/pages/map/map-interface/app-state/reducers/index.ts
index 70e1f3d9..f22305a5 100644
--- a/src/pages/map/map-interface/app-state/reducers/index.ts
+++ b/src/pages/map/map-interface/app-state/reducers/index.ts
@@ -10,6 +10,8 @@ import { contextPanelIsInitiallyOpen } from "../nav-hooks";
import { CoreAction, coreReducer } from "./core";
import { hashStringReducer } from "./hash-string";
import { AppAction, AppState, MenuAction, MenuState } from "./types";
+import { pathNameAction } from "../handlers/pathname";
+
export const browserHistory = createBrowserHistory();
const routerReducer = createRouterReducer(browserHistory);
@@ -30,6 +32,7 @@ const defaultState: AppState = {
core: coreReducer(undefined, { type: "init" }),
router: routerReducer(undefined, { type: "init" }),
menu: menuReducer(undefined, { type: "init" }),
+ nextRouterAction: null,
};
function mainReducer(
@@ -92,15 +95,38 @@ function mainReducer(
core: coreReducer(state.core, action as CoreAction),
menu: menuReducer(state.menu, action as MenuAction),
performance: performanceReducer(state.performance, action),
+ nextRouterAction: state.nextRouterAction,
};
}
}
-const appReducer = (state: AppState, action: AppAction) => {
+export default function appReducer(state: AppState, action: AppAction) {
// This might not be the right way to do hash management, but it
// centralizes the logic in one place.
- return hashStringReducer(mainReducer(state, action), action);
-};
+ return applyNextPath(
+ hashStringReducer(mainReducer(state, action), action),
+ action
+ );
+}
+
+const pathChangingActions: AppAction["type"][] = [
+ "set-menu-page",
+ "update-cross-section",
+ "update-state",
+ "start-map-query",
+ "close-infodrawer",
+];
+
+function applyNextPath(state: AppState, action: AppAction): AppState {
+ if (!pathChangingActions.includes(action.type)) return state;
+
+ const nextRouterAction = pathNameAction(state);
+ if (nextRouterAction == null) return state;
+ return {
+ ...state,
+ nextRouterAction,
+ };
+}
export function setInfoMarkerPosition(
state: AppState,
@@ -114,27 +140,6 @@ export function setInfoMarkerPosition(
let s1 = state;
- // //If we are on the column route, the column layer must be enabled
- // let s1 = state;
- // const colMatch = matchPath(
- // mapPagePrefix + "/loc/:lng/:lat/column",
- // pathname ?? state.router.location.pathname
- // );
- // if (colMatch != null) {
- // s1 = update(s1, { core: { mapLayers: { $add: [MapLayer.COLUMNS] } } });
- // }
-
- // // If we are disabling the column route, we should remove the column layer
- // const colMatch2 = matchPath(
- // mapPagePrefix + "/loc/:lng/:lat/column",
- // state.router.location.pathname
- // );
-
- // if (colMatch2 != null && colMatch == null) {
- // s1 = update(s1, { core: { mapLayers: { $remove: [MapLayer.COLUMNS] } } });
- // }
-
- // Set location
if (loc != null) {
const { lng, lat } = loc.params;
return {
@@ -177,74 +182,7 @@ export function setInfoMarkerPosition(
return state;
}
-export default appReducer;
export * from "./core";
export * from "./hash-string";
export * from "./map";
export * from "./types";
-
-/*
-function overallReducer(state: AppState, action: Action): AppState {
- let pos: MapPosition;
- if (action.type === "got-initial-map-state") {
- pos = action.data.mapPosition;
- } else if (action.type == "map-moved") {
- pos = action.data;
- }
-
- if (pos) {
- // You can access both app and globe states here
- const params = flyToParams(translateCameraPosition(pos));
- //console.log("Set globe position", destination);
- return {
- ...state,
- core: {
- ...state.core,
- mapPosition: pos,
- },
- globe: {
- ...state.globe,
- flyToProps: { ...params, duration: 0, once: true },
- },
- };
- }
-
- if (action.type == "map-loading" && !state.core.mapIsLoading) {
- return appReducer(state, {
- type: "reset-performance-counter",
- name: "map-loading",
- });
- }
- if (action.type == "map-idle" && state.core.mapIsLoading) {
- return appReducer(state, { type: "reset-performance-counter" });
- }
-
- switch (action.type) {
- case "@@router/ON_LOCATION_CHANGED":
- const isOpen = action.payload.location.pathname != "/";
- return {
- ...state,
- core: { ...state.core, menuOpen: isOpen, contextPanelOpen: isOpen },
- };
- case "got-initial-map-state":
- case "map-moved":
- return {
- ...state,
- core: {
- ...state.core,
- mapPosition: action.data,
- },
- };
- default:
- return state;
- }
-}
-
-const appReducer = reduceReducers(overallReducer, reducers);
-
-export type Action = CoreAction | MapAction | GlobeAction | RouterActions;
-
-export default appReducer;
-export * from "./core";
-export * from "./map";
-*/
diff --git a/src/pages/map/map-interface/app-state/reducers/types.ts b/src/pages/map/map-interface/app-state/reducers/types.ts
index e535329c..363214f5 100644
--- a/src/pages/map/map-interface/app-state/reducers/types.ts
+++ b/src/pages/map/map-interface/app-state/reducers/types.ts
@@ -24,6 +24,7 @@ export type AppState = {
core: CoreState;
router: ReduxRouterState;
menu: MenuState;
+ nextRouterAction: RouterActions | null;
};
type OverallActions = { type: "replace-state"; state: AppState };
diff --git a/src/pages/map/map-interface/components/info-drawer/macrostrat-linked.ts b/src/pages/map/map-interface/components/info-drawer/macrostrat-linked.ts
index b63f9945..f7cd676f 100644
--- a/src/pages/map/map-interface/components/info-drawer/macrostrat-linked.ts
+++ b/src/pages/map/map-interface/components/info-drawer/macrostrat-linked.ts
@@ -281,6 +281,7 @@ function LithTypes(props) {
[
lith_types.map((lithClass, i) => {
return h(LithologyTag, {
+ key: lithClass.name,
data: {
...lithClass,
},
diff --git a/src/pages/map/map-interface/index.ts b/src/pages/map/map-interface/index.ts
index 5c36deec..6a4d510e 100644
--- a/src/pages/map/map-interface/index.ts
+++ b/src/pages/map/map-interface/index.ts
@@ -1,6 +1,7 @@
import { ReduxRouter } from "@lagunovsky/redux-react-router";
import h from "@macrostrat/hyper";
import { Route, Routes } from "react-router-dom";
+import { useEffect } from "react";
import "~/styles/global.styl";
import "./searchbar.styl";
@@ -9,7 +10,13 @@ import "./ui-components.styl";
import { createRouterMiddleware } from "@lagunovsky/redux-react-router";
import { Provider } from "react-redux";
import { applyMiddleware, compose, createStore } from "redux";
-import reducerStack, { AppAction, AppState, browserHistory } from "./app-state";
+import reducerStack, {
+ AppAction,
+ AppState,
+ browserHistory,
+ useAppState,
+ useAppActions,
+} from "./app-state";
/** Redux is used only for the main map applicaton. This heavy state-management approach is
* essentially a legacy approach, and we are moving away from this in favor of more lightweight
@@ -19,6 +26,7 @@ import reducerStack, { AppAction, AppState, browserHistory } from "./app-state";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const routerMiddleware = createRouterMiddleware(browserHistory);
+
// Create the data store
let store = createStore(
reducerStack,
@@ -34,11 +42,23 @@ export default function MapApp({ routerBasename }) {
h(
ReduxRouter,
{ basename: routerBasename, store, history: browserHistory },
- [h(Routes, [h(Route, { path: "*", element: h(MapPage) })])]
+ [h(Routes, [h(Route, { path: "*", element: h(MapPage) })]), h(RouterSync)]
)
);
}
+function RouterSync() {
+ /** This is a temporary solution to sync the store with the history object. */
+ const nextRouterAction = useAppState((state) => state.nextRouterAction);
+ const runAction = useAppActions();
+ useEffect(() => {
+ if (nextRouterAction != null) {
+ runAction(nextRouterAction);
+ }
+ }, [nextRouterAction]);
+ return null;
+}
+
// Extend the window type to include the Redux DevTools types
declare global {
interface Window {
diff --git a/src/pages/map/map-interface/map-page/index.ts b/src/pages/map/map-interface/map-page/index.ts
index 3f31cab6..cb11397e 100644
--- a/src/pages/map/map-interface/map-page/index.ts
+++ b/src/pages/map/map-interface/map-page/index.ts
@@ -3,11 +3,10 @@ import { Suspense, useCallback, useEffect, useRef } from "react";
import { Spinner } from "@blueprintjs/core";
import loadable from "@loadable/component";
import { mapPagePrefix } from "@macrostrat-web/settings";
-import hyper from "@macrostrat/hyper";
import { MapAreaContainer } from "@macrostrat/map-interface";
import classNames from "classnames";
import { useSelector } from "react-redux";
-import { Route, Routes, useParams } from "react-router-dom";
+import { Route, Routes } from "react-router-dom";
import { useTransition } from "transition-hook";
import {
useAppActions,
@@ -18,14 +17,12 @@ import {
import Searchbar from "../components/navbar";
import MapContainer from "./map-view";
import { MenuPage } from "./menu";
-import { info } from "console";
+import h from "./main.module.styl";
const ElevationChart = loadable(() => import("../components/elevation-chart"));
const InfoDrawer = loadable(() => import("../components/info-drawer"));
const Menu = loadable(() => import("./menu"));
-import h from "./main.module.styl";
-
function MapView(props) {
return h(
Suspense,
@@ -34,12 +31,13 @@ function MapView(props) {
);
}
-export const MapPage = ({
+function MapPage({
baseRoute = "/",
menuPage = null,
}: {
+ baseRoute?: string;
menuPage?: MenuPage;
-}) => {
+}) {
const runAction = useAppActions();
const inputFocus = useAppState((s) => s.core.inputFocus);
const infoDrawerOpen = useAppState((s) => s.core.infoDrawerOpen);
@@ -92,7 +90,7 @@ export const MapPage = ({
},
[h("div.context-underlay", { onClick: onMouseDown }), h(MapView)]
);
-};
+}
function MapPageRoutes() {
return h(Routes, [
@@ -123,36 +121,8 @@ function InfoDrawerHolder() {
}),
}),
]),
- h(InfoDrawerLocationGrabber),
+ //h(InfoDrawerLocationGrabber),
]);
}
-function InfoDrawerLocationGrabber() {
- // We could probably do this in the reducer...
- const z = Math.round(
- useAppState((s) => s.core.mapPosition.target?.zoom) ?? 7
- );
- const infoMarkerPosition = useAppState((s) => s.core.infoMarkerPosition);
- const runAction = useAppActions();
-
- const { lat, lng } = infoMarkerPosition ?? {};
-
- // Todo: this is a pretty janky way to do state management
- useEffect(() => {
- if (lat == null || lng == null) return;
- runAction({
- type: "run-map-query",
- lat: Number(lat),
- lng: Number(lng),
- z,
- // Focused column or map unit from active layers.
- // This is a bit anachronistic, since we want to be
- // able to show columns that aren't necessarily shown on the map
- columns: [],
- map_id: null,
- });
- }, [lat, lng]);
- return null;
-}
-
export default MapPageRoutes;
diff --git a/src/pages/map/map-interface/map-page/settings-panel.module.styl b/src/pages/map/map-interface/map-page/settings-panel.module.styl
index 8e6e08f2..1b3c756b 100644
--- a/src/pages/map/map-interface/map-page/settings-panel.module.styl
+++ b/src/pages/map/map-interface/map-page/settings-panel.module.styl
@@ -16,11 +16,22 @@
font-size: 0.9em
line-height: 1em
+.dark-mode-controls
+ display: flex
+ gap: 0.5em
+ align-items: center
+ &>:first-child
+ flex-grow: 1
+
.settings
.auto-button
font-size: 12px
font-style: italic
+ &>*
+ width: 100%
+ margin: 0.1em 0 0.2em
+
:global
.bp5-control
display: flex
@@ -34,8 +45,6 @@
position: relative
.bp5-button, .bp5-button-group
- width: 100%
- margin: 0.1em 0 0.2em
justify-content: start
align-items: center
//box-shadow: 0 0 0px 1px var(--card-shadow-color)
@@ -52,6 +61,9 @@
.callout-panel
border-radius: 3px
overflow: hidden
+ .callout-header
+ :global(.bp5-button)
+ width: 100%
&.expanded
.callout-header
:global(.bp5-button)
@@ -78,3 +90,6 @@
gap: 0.5em
&>*
flex: 1
+
+
+
diff --git a/src/pages/map/map-interface/map-page/settings-panel.ts b/src/pages/map/map-interface/map-page/settings-panel.ts
index 1940b692..57b60bc7 100644
--- a/src/pages/map/map-interface/map-page/settings-panel.ts
+++ b/src/pages/map/map-interface/map-page/settings-panel.ts
@@ -4,6 +4,7 @@
import {
AnchorButton,
Button,
+ ButtonGroup,
Callout,
Collapse,
Icon,
@@ -12,7 +13,6 @@ import {
Switch,
Tag,
} from "@blueprintjs/core";
-import hyper from "@macrostrat/hyper";
import { applyMapPositionToHash } from "@macrostrat/map-interface";
import {
DarkModeButton,
@@ -27,9 +27,7 @@ import {
useAppState,
} from "~/pages/map/map-interface/app-state";
-import styles from "./settings-panel.module.styl";
-
-const h = hyper.styled(styles);
+import h from "./settings-panel.module.styl";
const ExperimentsPanel = (props) => {
const dispatch = useAppActions();
@@ -198,33 +196,30 @@ function ThemeButton() {
const update = darkModeUpdater();
const icon = darkMode.isAutoset ? "tick" : "desktop";
- const autoButton = h(
- Button,
- {
- minimal: true,
- active: darkMode.isAutoset,
- rightIcon: h(Icon, { icon, size: 12 }),
- intent: darkMode.isAutoset ? "success" : "primary",
- className: "auto-button sub-button",
- small: true,
- onClick(evt) {
- if (darkMode.isAutoset) return;
- evt.stopPropagation();
- update(null);
- },
- },
-
- "auto"
- );
-
const darkModeText = darkMode.isEnabled
? "Turn on the lights"
: "Turn off the lights";
return h("div.dark-mode-controls", [
+ h(DarkModeButton, { minimal: true, active: false, allowReset: true }, [
+ h("span.text", darkModeText),
+ ]),
h(
- DarkModeButton,
- { minimal: true, active: false, allowReset: true, rightIcon: autoButton },
- [h("span.text", darkModeText)]
+ Button,
+ {
+ minimal: true,
+ active: darkMode.isAutoset,
+ rightIcon: h(Icon, { icon, size: 12 }),
+ intent: darkMode.isAutoset ? "success" : "primary",
+ className: "auto-button sub-button",
+ small: true,
+ onClick(evt) {
+ if (darkMode.isAutoset) return;
+ evt.stopPropagation();
+ update(null);
+ },
+ },
+
+ "auto"
),
]);
}