Skip to content

Commit

Permalink
Save/Load location from local storage
Browse files Browse the repository at this point in the history
Fixes #187
  • Loading branch information
starsep committed Dec 27, 2023
1 parent d43b5bf commit 429aa66
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 73 deletions.
82 changes: 18 additions & 64 deletions src/components/map.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import { useAppContext } from "~/appContext";
import { fetchCountriesData, fetchNodeDataFromBackend } from "~/backend";
import nominatimGeocoder from "~/components/nominatimGeocoder";
import { useLanguage } from "~/i18n";
import {
addNodeIdToHash,
getMapLocation,
locationParameter,
parseParametersFromUrl,
removeNodeIdFromHash,
saveLocationToLocalStorage,
} from "~/location";
import ButtonsType from "~/model/buttonsType";
import { DefibrillatorData } from "~/model/defibrillatorData";
import { ModalType, initialModalState } from "~/model/modal";
Expand Down Expand Up @@ -62,21 +70,6 @@ function fillSidebarWithOsmDataAndShow(
});
}

function parseHash(): Record<string, string> {
const parameters: Record<string, string> = {};
for (const part of window.location.hash.slice(1).split("&")) {
const [key, value] = part.split("=", 2);
parameters[key] = value;
}
return parameters;
}

function getNewHashString(parameters: Record<string, string>) {
return Object.entries(parameters)
.map(([key, value]) => `${key}=${value}`)
.join("&");
}

const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
const {
authState: { auth },
Expand All @@ -92,41 +85,15 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
} = useAppContext();
const { t } = useTranslation();
const language = useLanguage();

const hash4MapName = "map";

const paramsFromHash = parseHash();

let initialLongitude = -8;
let initialLatitude = 47.74;
let initialZoom = 3;

if (paramsFromHash[hash4MapName]) {
[initialZoom, initialLatitude, initialLongitude] = paramsFromHash[
hash4MapName
]
.split("/")
.map(Number);
}

const { longitude, latitude, zoom } = getMapLocation();
const mapContainer = useRef<HTMLDivElement | null>(null);
const mapRef = useRef<maplibregl.Map | null>(null);
const maplibreGeocoderRef = useRef<MaplibreGeocoder | null>(null);
const controlsLocation = "bottom-right";

const [marker, setMarker] = useState<maplibregl.Marker | null>(null);

const [sidebarLeftShown, setSidebarLeftShown] = useState(false);

const [footerButtonType, setFooterButtonType] = useState(ButtonsType.Basic);

const removeNodeIdFromHash = () => {
const hashParams = parseHash();
// biome-ignore lint/performance/noDelete: using undefined assignment causes it to be part of url
delete hashParams.node_id;
window.location.hash = getNewHashString(hashParams);
};

const deleteMarker = () => {
if (marker !== null) {
marker.remove();
Expand Down Expand Up @@ -225,11 +192,10 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
if (mapRef.current !== null) return; // stops map from initializing more than once
const map = new maplibregl.Map({
container: mapContainer.current,
hash: hash4MapName,
// @ts-ignore
hash: locationParameter,
style: mapStyle(language.toUpperCase(), countriesData),
center: [initialLongitude, initialLatitude],
zoom: initialZoom,
center: [longitude, latitude],
zoom: zoom,
minZoom: 3,
maxZoom: 19,
maplibreLogo: false,
Expand Down Expand Up @@ -286,6 +252,7 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
map.on("mouseleave", "unclustered-low-zoom", () => {
map.getCanvas().style.cursor = "";
});
map.on("moveend", saveLocationToLocalStorage);

// biome-ignore lint/suspicious/noExplicitAny: unknown type
type MapEventType = any;
Expand Down Expand Up @@ -313,14 +280,8 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
});
});
function showObjectWithProperties(e: MapEventType) {
console.log(
"Clicked on object with properties: ",
e.features[0].properties,
);
if (e.features[0].properties !== undefined && mapRef.current !== null) {
const osmNodeId = e.features[0].properties.node_id;
console.log("Clicked on object with osm_id: ", osmNodeId);
// show sidebar
fillSidebarWithOsmDataAndShow(
osmNodeId,
mapRef.current,
Expand All @@ -329,13 +290,7 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
setSidebarLeftShown,
false,
);
// update hash
const params = {
...parseHash(),
node_id: osmNodeId,
};
console.log("new hash params", params);
window.location.hash = getNewHashString(params);
addNodeIdToHash(osmNodeId);
}
}

Expand All @@ -344,7 +299,7 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
map.on("click", "unclustered-low-zoom", showObjectWithProperties);

// if direct link to osm node then get its data and zoom in
const newParamsFromHash = parseHash();
const newParamsFromHash = parseParametersFromUrl();
if (newParamsFromHash.node_id && mapRef.current !== null) {
fillSidebarWithOsmDataAndShow(
newParamsFromHash.node_id,
Expand All @@ -356,9 +311,9 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
);
}
}, [
initialLatitude,
initialLongitude,
initialZoom,
latitude,
longitude,
zoom,
setSidebarAction,
setSidebarData,
language,
Expand All @@ -371,7 +326,6 @@ const MapView: FC<MapViewProps> = ({ openChangesetId, setOpenChangesetId }) => {
const map = mapRef.current;
addMaplibreGeocoder(map);
if (countriesDataLanguage !== language) return; // wait for countries data to be loaded
// @ts-ignore
map.setStyle(mapStyle(language.toUpperCase(), countriesData));
}, [countriesData, countriesDataLanguage, language]);

Expand Down
7 changes: 5 additions & 2 deletions src/components/map_style.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StyleSpecification } from "maplibre-gl";
import { backendBaseUrl } from "~/backend";
import { Country } from "~/model/country";

Expand All @@ -8,7 +9,10 @@ const spriteUrl = new URL("img/sprite", baseUrl).href;
const tilesUrl = `${backendBaseUrl}/api/v1/tile/{z}/{x}/{y}.mvt`;
const TILE_COUNTRIES_MAX_ZOOM = 6;

const mapStyle = (lang: string, countriesData: Array<Country>) => {
const mapStyle = (
lang: string,
countriesData: Array<Country>,
): StyleSpecification => {
const countryCodeToName: Record<string, string> = countriesData.reduce(
(map: Record<string, string>, country) => {
if (country.names[lang.toUpperCase()] !== undefined) {
Expand Down Expand Up @@ -217,7 +221,6 @@ const mapStyle = (lang: string, countriesData: Array<Country>) => {
},
},
],
id: "style",
};
};

Expand Down
81 changes: 81 additions & 0 deletions src/location.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
export const locationParameter = "map";

export interface MapLocation {
zoom: number;
latitude: number;
longitude: number;
}

export function getMapLocation(): MapLocation {
const defaultLocation: MapLocation = {
zoom: 3,
latitude: 47.74,
longitude: -8,
};
return (
getLocationFromUrl() ?? getLocationFromLocalStorage() ?? defaultLocation
);
}

function getLocationStringFromUrl(): string | null {
const paramsFromHash = parseParametersFromUrl();
return paramsFromHash[locationParameter] ?? null;
}

function getLocationFromUrl(): MapLocation | null {
const locationString = getLocationStringFromUrl();
return locationString !== null
? parseLocationFromString(locationString)
: null;
}

function getLocationFromLocalStorage(): MapLocation | null {
const locationString = localStorage.getItem(locationParameter);
return locationString !== null
? parseLocationFromString(locationString)
: null;
}

function parseLocationFromString(locationString: string): MapLocation | null {
try {
const [zoom, latitude, longitude] = locationString.split("/").map(Number);
return { zoom, latitude, longitude };
} catch (e) {
return null;
}
}

export function parseParametersFromUrl(): Record<string, string> {
const parameters: Record<string, string> = {};
for (const part of window.location.hash.slice(1).split("&")) {
const [key, value] = part.split("=", 2);
parameters[key] = value;
}
return parameters;
}

function serializeParametersToUrlTarget(parameters: Record<string, string>) {
return Object.entries(parameters)
.map(([key, value]) => `${key}=${value}`)
.join("&");
}

export function removeNodeIdFromHash() {
const params = parseParametersFromUrl();
// biome-ignore lint/performance/noDelete: using undefined assignment causes it to be part of url
delete params.node_id;
window.location.hash = serializeParametersToUrlTarget(params);
}

export function addNodeIdToHash(nodeId: string) {
const params = parseParametersFromUrl();
params.node_id = nodeId;
window.location.hash = serializeParametersToUrlTarget(params);
}

export function saveLocationToLocalStorage() {
const location = getLocationStringFromUrl();
if (location !== null) {
localStorage.setItem(locationParameter, location ?? "");
}
}
7 changes: 0 additions & 7 deletions types/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,3 @@ declare module "*.svg" {
const content: React.FunctionComponent<React.SVGAttributes<SVGElement>>;
export default content;
}

declare module "@mapbox/timespace" {
export function getFuzzyLocalTimeFromPoint(
timestamp: number,
point: number[],
): { _d: Date };
}

0 comments on commit 429aa66

Please sign in to comment.