diff --git a/package-lock.json b/package-lock.json index 74bc7656..d221c0c1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,11 +14,11 @@ "compressorjs": "^1.1.1", "history": "^5.3.0", "leaflet": "^1.9.3", - "leaflet.gridlayer.googlemutant": "^0.13.5", "preact": "^10.11.0", "preact-i18nline": "^2.0.0", "preact-router": "^4.1.0", "react-hook-form": "^7.36.1", + "react-leaflet": "^4.2.1", "react-redux": "^8.0.4", "react-router-redux": "^4.0.8", "react-use": "^17.4.0", @@ -44,7 +44,6 @@ "@trivago/prettier-plugin-sort-imports": "^3.3.0", "@types/jest": "^29.2.4", "@types/leaflet": "^1.9.0", - "@types/leaflet.gridlayer.googlemutant": "^0.4.6", "@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/parser": "^5.46.0", "babel-jest": "^29.5.0", @@ -4090,6 +4089,16 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -7957,15 +7966,6 @@ "@types/geojson": "*" } }, - "node_modules/@types/leaflet.gridlayer.googlemutant": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/@types/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.4.6.tgz", - "integrity": "sha512-L0J7NadcZp5bcKQrv4DVlsEbQ90xLsOKScckAMnxoghh/wogk0GVkauYOYHBKeKDkx9qkMRzTf8oO+fKeYD7oQ==", - "dev": true, - "dependencies": { - "@types/leaflet": "*" - } - }, "node_modules/@types/lodash": { "version": "4.14.185", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.185.tgz", @@ -22024,11 +22024,6 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz", "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==" }, - "node_modules/leaflet.gridlayer.googlemutant": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.13.5.tgz", - "integrity": "sha512-DHUEXpo1t0WZ9tpdLUHHkrTK7LldCXr/gqkV5E/4hHidDJB2srceLLCZj4PV/pl0jPdTkVBT+Lr8nL9EZDB5hw==" - }, "node_modules/less": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", @@ -27908,6 +27903,19 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "node_modules/react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "dependencies": { + "@react-leaflet/core": "^2.1.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/react-redux": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", @@ -37834,6 +37842,12 @@ "@prefresh/utils": "^1.1.2" } }, + "@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "requires": {} + }, "@rollup/plugin-babel": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -40623,15 +40637,6 @@ "@types/geojson": "*" } }, - "@types/leaflet.gridlayer.googlemutant": { - "version": "0.4.6", - "resolved": "https://registry.npmjs.org/@types/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.4.6.tgz", - "integrity": "sha512-L0J7NadcZp5bcKQrv4DVlsEbQ90xLsOKScckAMnxoghh/wogk0GVkauYOYHBKeKDkx9qkMRzTf8oO+fKeYD7oQ==", - "dev": true, - "requires": { - "@types/leaflet": "*" - } - }, "@types/lodash": { "version": "4.14.185", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.185.tgz", @@ -51715,11 +51720,6 @@ "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz", "integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==" }, - "leaflet.gridlayer.googlemutant": { - "version": "0.13.5", - "resolved": "https://registry.npmjs.org/leaflet.gridlayer.googlemutant/-/leaflet.gridlayer.googlemutant-0.13.5.tgz", - "integrity": "sha512-DHUEXpo1t0WZ9tpdLUHHkrTK7LldCXr/gqkV5E/4hHidDJB2srceLLCZj4PV/pl0jPdTkVBT+Lr8nL9EZDB5hw==" - }, "less": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/less/-/less-4.1.3.tgz", @@ -56208,6 +56208,14 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" }, + "react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "requires": { + "@react-leaflet/core": "^2.1.0" + } + }, "react-redux": { "version": "8.0.4", "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-8.0.4.tgz", diff --git a/package.json b/package.json index 2df574d7..ea9f4396 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,6 @@ "@trivago/prettier-plugin-sort-imports": "^3.3.0", "@types/jest": "^29.2.4", "@types/leaflet": "^1.9.0", - "@types/leaflet.gridlayer.googlemutant": "^0.4.6", "@typescript-eslint/eslint-plugin": "^5.46.0", "@typescript-eslint/parser": "^5.46.0", "babel-jest": "^29.5.0", @@ -87,11 +86,11 @@ "compressorjs": "^1.1.1", "history": "^5.3.0", "leaflet": "^1.9.3", - "leaflet.gridlayer.googlemutant": "^0.13.5", "preact": "^10.11.0", "preact-i18nline": "^2.0.0", "preact-router": "^4.1.0", "react-hook-form": "^7.36.1", + "react-leaflet": "^4.2.1", "react-redux": "^8.0.4", "react-router-redux": "^4.0.8", "react-use": "^17.4.0", diff --git a/plugins/lime-plugin-locate/index.ts b/plugins/lime-plugin-locate/index.ts index 6dce467f..fc2045db 100755 --- a/plugins/lime-plugin-locate/index.ts +++ b/plugins/lime-plugin-locate/index.ts @@ -1,19 +1,8 @@ -import * as constants from "./src/locateConstants"; -import epics from "./src/locateEpics"; import { LocateMenu } from "./src/locateMenu"; import Locate from "./src/locatePage"; -import { reducer } from "./src/locateReducer"; -import * as selector from "./src/locateSelectors"; export default { name: "Locate", page: Locate, menu: LocateMenu, - store: { - name: "locate", - epics, - reducer, - selector, - constants, - }, } as LimePlugin; diff --git a/plugins/lime-plugin-locate/src/assetsUtils.js b/plugins/lime-plugin-locate/src/leafletUtils.ts similarity index 81% rename from plugins/lime-plugin-locate/src/assetsUtils.js rename to plugins/lime-plugin-locate/src/leafletUtils.ts index 3b6fbc91..e0aa70f0 100644 --- a/plugins/lime-plugin-locate/src/assetsUtils.js +++ b/plugins/lime-plugin-locate/src/leafletUtils.ts @@ -1,13 +1,17 @@ +import { DivIconOptions, IconOptions } from "leaflet"; + +const leafletVersion = "1.9.3"; + function loadLeafletScript() { return new Promise((res, rej) => { if (document.getElementById("leaflet-script")) { + // @ts-ignore res(); } else { const script = document.createElement("script"); script.onload = res; script.onerror = rej; - script.src = - "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.3.0/leaflet.js"; + script.src = `https://unpkg.com/leaflet@${leafletVersion}/dist/leaflet.js`; script.id = "leaflet-script"; document.body.appendChild(script); } @@ -17,13 +21,14 @@ function loadLeafletScript() { function loadLeafletStylesheet() { return new Promise((res, rej) => { if (document.getElementById("leaflet-style")) { + // @ts-ignore res(); } else { const style = document.createElement("link"); style.onload = res; style.onerror = rej; style.rel = "stylesheet"; - style.href = "https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"; + style.href = `https://unpkg.com/leaflet@${leafletVersion}/dist/leaflet.css`; style.id = "leaflet-style"; document.head.appendChild(style); } @@ -35,26 +40,10 @@ export function loadLeafLet() { return loadLeafletStylesheet().then(loadLeafletScript); } -export function loadGoogleMapsApi() { - return new Promise((res, rej) => { - if (document.getElementById("googlemaps-script")) { - res(); - } else { - const key = "AIzaSyBS0M7H7Ltk1ipjwqi8r9_WQJOzWfav4Ok"; - const script = document.createElement("script"); - script.onload = res; - script.onerror = rej; - script.src = `https://maps.googleapis.com/maps/api/js?key=${key}`; - script.id = "googlemaps-script"; - document.body.appendChild(script); - } - }); -} - -export const homeIcon = { +export const homeIcon: IconOptions = { iconUrl: "data:image/png;base64,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", - iconsSize: [25, 41], + iconSize: [25, 41], iconAnchor: [13, 40], popupAnchor: [0, -45], }; diff --git a/plugins/lime-plugin-locate/src/locateActions.js b/plugins/lime-plugin-locate/src/locateActions.js deleted file mode 100755 index fb99d882..00000000 --- a/plugins/lime-plugin-locate/src/locateActions.js +++ /dev/null @@ -1,30 +0,0 @@ -import { - LOCATION_CHANGE, - LOCATION_LOAD, - LOCATION_LOAD_LINKS, - LOCATION_TOOGLE_EDIT, - LOCATION_USER_SET, -} from "./locateConstants"; - -export const loadLocationLinks = () => ({ - type: LOCATION_LOAD_LINKS, -}); - -export const toogleEdit = (status) => ({ - type: LOCATION_TOOGLE_EDIT, - payload: status, -}); - -export const loadLocation = () => ({ - type: LOCATION_LOAD, -}); - -export const changeLocation = (location) => ({ - type: LOCATION_CHANGE, - payload: location, -}); - -export const setUserLocation = (location) => ({ - type: LOCATION_USER_SET, - payload: location, -}); diff --git a/plugins/lime-plugin-locate/src/locateApi.js b/plugins/lime-plugin-locate/src/locateApi.js index 9d427aac..3e2d5428 100644 --- a/plugins/lime-plugin-locate/src/locateApi.js +++ b/plugins/lime-plugin-locate/src/locateApi.js @@ -1,10 +1,13 @@ -export const getLocation = (api) => api.call("lime-location", "get", {}); +import api from "utils/uhttpd.service"; -export const getNodesandlinks = (api) => +export const getLocation = () => api.call("lime-location", "get", {}); + +export const getNodesandlinks = async () => api.call("lime-location", "all_nodes_and_links", {}); -export const changeLocation = (api, location) => - api.call("lime-location", "set", { +export const changeLocation = async (location) => { + return await api.call("lime-location", "set", { lat: location.lat.toFixed(5), lon: location.lon.toFixed(5), }); +}; diff --git a/plugins/lime-plugin-locate/src/locateConstants.js b/plugins/lime-plugin-locate/src/locateConstants.js deleted file mode 100644 index bb15a90c..00000000 --- a/plugins/lime-plugin-locate/src/locateConstants.js +++ /dev/null @@ -1,9 +0,0 @@ -export const LOCATION_LOAD = "locate/LOCATION_LOAD"; -export const LOCATION_LOAD_SUCCESS = "locate/LOCATION_LOAD_SUCCESS"; -export const LOCATION_CHANGE = "locate/LOCATION_CHANGE"; -export const LOCATION_CHANGE_SUCCESS = "locate/LOCATION_CHANGE_SUCCESS"; -export const LOCATION_USER_SET = "locate/LOCATION_USER_SET"; -export const LOCATION_LOAD_LINKS = "locate/LOCATION_LOAD_LINKS"; -export const LOCATION_LOAD_LINKS_SUCCESS = "locate/LOCATION_LOAD_LINKS_SUCCESS"; -export const LOCATION_LOAD_LINKS_ERROR = "locate/LOCATION_LOAD_LINKS_ERROR"; -export const LOCATION_TOOGLE_EDIT = "locate/LOCATION_TOOGLE_EDIT"; diff --git a/plugins/lime-plugin-locate/src/locateEpics.js b/plugins/lime-plugin-locate/src/locateEpics.js deleted file mode 100644 index e5aa4b58..00000000 --- a/plugins/lime-plugin-locate/src/locateEpics.js +++ /dev/null @@ -1,41 +0,0 @@ -import { ofType } from "redux-observable"; -import { map, mergeMap } from "rxjs/operators"; - -import { changeLocation, getLocation, getNodesandlinks } from "./locateApi"; -import { - LOCATION_CHANGE, - LOCATION_CHANGE_SUCCESS, - LOCATION_LOAD, - LOCATION_LOAD_LINKS, - LOCATION_LOAD_LINKS_ERROR, - LOCATION_LOAD_LINKS_SUCCESS, - LOCATION_LOAD_SUCCESS, -} from "./locateConstants"; - -// LOAD INTERFACES -> Dispatch success and stations loads -const locateLoad = (action$, _state$, { wsAPI }) => - action$.pipe( - ofType(LOCATION_LOAD, LOCATION_CHANGE_SUCCESS), - mergeMap(() => getLocation(wsAPI)), - map((payload) => ({ type: LOCATION_LOAD_SUCCESS, payload })) - ); - -const locateChange = (action$, _state$, { wsAPI }) => - action$.pipe( - ofType(LOCATION_CHANGE), - mergeMap((action) => changeLocation(wsAPI, action.payload)), - map(() => ({ type: LOCATION_CHANGE_SUCCESS })) - ); - -const locateLoadlinks = (action$, _state$, { wsAPI }) => - action$.pipe( - ofType(LOCATION_LOAD_LINKS), - mergeMap(() => getNodesandlinks(wsAPI)), - map((payload = {}) => - payload.result - ? { type: LOCATION_LOAD_LINKS_SUCCESS, payload: payload.result } - : { type: LOCATION_LOAD_LINKS_ERROR } - ) - ); - -export default { locateLoad, locateChange, locateLoadlinks }; diff --git a/plugins/lime-plugin-locate/src/locatePage.tsx b/plugins/lime-plugin-locate/src/locatePage.tsx index 67391b8d..cc65c67f 100644 --- a/plugins/lime-plugin-locate/src/locatePage.tsx +++ b/plugins/lime-plugin-locate/src/locatePage.tsx @@ -1,58 +1,30 @@ -/* eslint-disable no-undef */ import { Trans } from "@lingui/macro"; -import { useEffect, useState } from "preact/hooks"; -import { connect } from "react-redux"; -import { bindActionCreators } from "redux"; +import L, { LatLngExpression, icon } from "leaflet"; +import { useEffect, useRef, useState } from "preact/hooks"; +import { LayersControl, MapContainer, Marker, TileLayer } from "react-leaflet"; import { Loading } from "components/loading"; +import { + useChangeLocation, + useLoadLeaflet, + useLocation, + useNodesandlinks, +} from "plugins/lime-plugin-locate/src/locateQueries"; + import { useBoardData } from "utils/queries"; -import { homeIcon, loadGoogleMapsApi, loadLeafLet } from "./assetsUtils"; import { getCommunityGeoJSON } from "./communityGeoJSON"; -import { - changeLocation, - loadLocation, - loadLocationLinks, - toogleEdit, -} from "./locateActions"; -import { getLat, getLon, isCommunityLocation } from "./locateSelectors"; +import { homeIcon } from "./leafletUtils"; import style from "./style.less"; -import L from "leaflet"; const openStreetMapTileString = "http://{s}.tile.osm.org/{z}/{x}/{y}.png"; const openStreetMapAttribution = '© OpenStreetMap contributors'; -function setupMap() { - /** Initialize the leaflet map */ - const map = L.map("map-container"); - // @ts-ignore - window.map = map; - // Load layers - require("leaflet.gridlayer.googlemutant"); - const satellite = L.gridLayer.googleMutant({ type: "satellite" }); - const hybrid = L.gridLayer.googleMutant({ type: "hybrid" }); - const osm = L.tileLayer(openStreetMapTileString, { - attribution: openStreetMapAttribution, - }); - // Add layers controller on bottom right corner - L.control - .layers( - { - "Open Street Map": osm, - "Google Maps Satellite": satellite, - "Google Maps Hybrid": hybrid, - }, - {}, - { position: "bottomright" } - ) - .addTo(map); - // Setup Open Street Map as base layer - osm.addTo(map); - return map; -} - +const gmSatellite = "https://{s}.google.com/vt/lyrs=s&x={x}&y={y}&z={z}"; +const gmHybrid = "https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}"; +const gmSubdomains = ["mt0", "mt1", "mt2", "mt3"]; function getCommunityLayer(nodeHostname, stationLat, stationLon, nodesData) { /** Create a Leaflet layer with community nodes and links to be added to the map*/ @@ -63,108 +35,88 @@ function getCommunityLayer(nodeHostname, stationLat, stationLon, nodesData) { }; } // Get community GeoJSON, filter out nodes in same location as station host. - let geoJSON = getCommunityGeoJSON(nodesData, [stationLon, stationLat]); - const layer = L.geoJSON(geoJSON, { + const geoJSON = getCommunityGeoJSON(nodesData, [stationLon, stationLat]); + return L.geoJSON(geoJSON, { onEachFeature: (feature, layer) => { if (feature.properties && feature.properties.name) { layer.bindTooltip(feature.properties.name).openTooltip(); } }, }); - return layer; } -type LocatePageType = { - editting: boolean; - submitting: boolean; - stationLat?: number; - stationLon?: number; - nodesData: Object; - isCommunityLocation: boolean; - loadLocation?: () => void; - loadLocationLinks?: () => void; - changeLocation?: ({ lat, lon }: { lat: number, lon: number}) => {} ; - toogleEdit?: (b: boolean) => {} ; -} - -export const LocatePage = ({ - editting, - submitting, - stationLat, - stationLon, - nodesData, - isCommunityLocation, - loadLocation, - loadLocationLinks, - changeLocation, - toogleEdit, -}: LocatePageType) => { +export const LocatePage = () => { const { data: boardData } = useBoardData(); - const [loading, setLoading] = useState(true); - const [assetError, setAssetError] = useState(false); - const [map, setMap] = useState(null); - const [nodeMarker, setNodeMarker] = useState(null); + const { + isError: isAssetError, + isFetchedAfterMount: assetsLoaded, + isLoading: isLoadingAssets, + } = useLoadLeaflet({ + refetchOnWindowFocus: false, + }); + + const { + data: nodeLocation, + isLoading: isLoadingLocation, + isFetched: locationLoaded, + } = useLocation({ + enabled: assetsLoaded, + }); + + const { data: nodesData } = useNodesandlinks({ + enabled: locationLoaded, + }); + + const { mutate: changeLocation, isLoading: submitting } = useChangeLocation( + { + onSettled: () => { + toogleEdition(); + }, + } + ); + + const loading = isLoadingLocation || isLoadingAssets; + const isCommunityLocation = nodeLocation.default; + const stationLat = + nodeLocation.location.lat !== "FIXME" + ? nodeLocation.location.lat + : null; + const stationLon = + nodeLocation.location.lon !== "FIXME" + ? nodeLocation.location.lon + : null; + const hasLocation = stationLat && !isCommunityLocation; + + const [editting, setEditting] = useState(false); + const [nodeMarker, setNodeMarker] = useState(null); const [communityLayer, setCommunityLayer] = useState(null); - // Load third parties assests in component mount - useEffect(() => { - Promise.all([loadLeafLet(), loadGoogleMapsApi()]) - .then(onAssetsLoad) // Setup the map - .catch(onAssetsError) - .then(loadLocation) // Load node location - .then(loadLocationLinks); // Load community locations - }, [loadLocation, loadLocationLinks]); + const mapRef = useRef(); // Set map position when map is available or location gets updated useEffect(() => { function updateNodeMarker(lat, lon) { - if (nodeMarker) { - nodeMarker.setLatLng([lat, lon]); - } else { - const marker = L.marker([lat, lon], { - // @ts-ignore - icon: L.icon(homeIcon), - alt: "node marker", - }).addTo(map); - setNodeMarker(marker); - } + setNodeMarker([lat, lon]); } + const mapInstance = mapRef.current; - if (map && stationLat) { - map.setView([stationLat, stationLon], 13); + if (!loading && mapInstance && stationLat) { + mapInstance.setView([+stationLat, +stationLon], 13); updateNodeMarker(stationLat, stationLon); - } else if (map) { - map.setView([-30, -60], 3); } - }, [stationLat, stationLon, map, nodeMarker]); + }, [stationLat, stationLon, loading]); // Center the map on the node also when editting is turned on useEffect(() => { + const map = mapRef.current; if (map && stationLat) { - editting && map.setView([stationLat, stationLon], 13); + editting && map.setView([+stationLat, +stationLon], 13); } - }, [map, editting, stationLat, stationLon]); - - function onAssetsLoad() { - // A promise to avoid raise condition between loadLocation and onAssetLoad - return new Promise((resolve) => { - const map = setupMap(); - setLoading(false); - setMap(map); - resolve(); - }); - } - - function onAssetsError() { - setLoading(false); - setAssetError(true); - } + }, [mapRef, editting, stationLat, stationLon]); function onConfirmLocation() { - const position = map.getCenter(); - const lat = position.lat_neg ? position.lat * -1 : position.lat; - const lon = position.lng_neg ? position.lng * -1 : position.lng; - if (changeLocation) changeLocation({ lat, lon }); + const position = mapRef.current.getCenter(); + changeLocation({ lat: position.lat, lon: position.lng }); if (communityLayer) { // Hide the community view, to avoid outdated links toogleCommunityLayer(); @@ -173,7 +125,7 @@ export const LocatePage = ({ function toogleCommunityLayer() { if (communityLayer) { - map.removeLayer(communityLayer); + mapRef.current.removeLayer(communityLayer); setCommunityLayer(null); } else { const layer = getCommunityLayer( @@ -182,7 +134,7 @@ export const LocatePage = ({ stationLon, nodesData ); - layer.addTo(map); + layer.addTo(mapRef.current); setCommunityLayer(layer); } } @@ -191,17 +143,11 @@ export const LocatePage = ({ return !loading && typeof stationLat !== "undefined"; } - const hasLocation = stationLat && !isCommunityLocation; - - function toogleEditFalse() { - if (toogleEdit) toogleEdit(false); - } - - function toogleEditTrue() { - if (toogleEdit) toogleEdit(true); + function toogleEdition() { + setEditting(!editting); } - if (assetError) { + if (isAssetError) { return (
Cannot load map, check your internet connection @@ -210,47 +156,61 @@ export const LocatePage = ({ } return ( -
-
- {(!isReady() || submitting) && ( -
- -
- )} - {editting && ( -
- )} -
+ <> + {(!isReady() || submitting) && ( +
+ +
+ )} + {isReady() && ( + + + + + + + + + + + + + {nodeMarker && ( + + )} + {editting && ( +
+ )} + + )} {isReady() && (
- {/* Actions while editting */} {editting && ( )} - {editting && ( - - )} - {/* Actions while not editting */} - {!editting && hasLocation && ( - - )} - {!editting && !hasLocation && ( - - )} {!editting && ( )} + +
)} -
+ ); }; - -const mapStateToProps = (state) => ({ - stationLat: getLat(state), - stationLon: getLon(state), - isCommunityLocation: isCommunityLocation(state), - nodesData: state.locate.nodesData, - submitting: state.locate.submitting, - editting: state.locate.editting, -}); - - -const mapDispatchToProps = (dispatch) => ({ - loadLocation: bindActionCreators(loadLocation, dispatch), - loadLocationLinks: bindActionCreators(loadLocationLinks, dispatch), - changeLocation: bindActionCreators(changeLocation, dispatch), - toogleEdit: bindActionCreators(toogleEdit, dispatch), -}); - -export default connect(mapStateToProps, mapDispatchToProps)(LocatePage); +export default LocatePage; diff --git a/plugins/lime-plugin-locate/src/locateQueries.tsx b/plugins/lime-plugin-locate/src/locateQueries.tsx new file mode 100644 index 00000000..45c1f399 --- /dev/null +++ b/plugins/lime-plugin-locate/src/locateQueries.tsx @@ -0,0 +1,74 @@ +import { useMutation, useQuery } from "@tanstack/react-query"; + +import { loadLeafLet } from "plugins/lime-plugin-locate/src/leafletUtils"; +import { + changeLocation, + getLocation, + getNodesandlinks, +} from "plugins/lime-plugin-locate/src/locateApi"; + +import queryCache from "utils/queryCache"; + +export interface INodeLocation { + location: { + lon: string; + lat: string; + }; + default: boolean; +} + +export function useLocation(params) { + return useQuery(["lime-location", "get"], getLocation, { + placeholderData: { + default: false, + location: { + lon: "FIXME", + lat: "FIXME", + }, + }, + ...params, + }); +} + +export function useNodesandlinks(params) { + return useQuery( + ["lime-location", "all_nodes_and_links"], + getNodesandlinks, + { + ...params, + } + ); +} + +interface IChangeUserParams { + lat: number; + lon: number; +} + +export function useChangeLocation(params) { + return useMutation({ + mutationFn: changeLocation, + onSuccess: (data: { lat: string; lon: string }) => { + queryCache.setQueryData( + ["lime-location", "get"], + (oldData: INodeLocation) => + oldData + ? { + ...oldData, + location: { + lat: data.lat, + lon: data.lon, + }, + } + : oldData + ); + }, + ...params, + }); +} + +export function useLoadLeaflet(params) { + return useQuery(["lime-location", "load_leaflet"], loadLeafLet, { + ...params, + }); +} diff --git a/plugins/lime-plugin-locate/src/locateReducer.js b/plugins/lime-plugin-locate/src/locateReducer.js deleted file mode 100644 index 5864e51f..00000000 --- a/plugins/lime-plugin-locate/src/locateReducer.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - LOCATION_CHANGE, - LOCATION_CHANGE_SUCCESS, - LOCATION_LOAD_LINKS_SUCCESS, - LOCATION_LOAD_SUCCESS, - LOCATION_TOOGLE_EDIT, - LOCATION_USER_SET, -} from "./locateConstants"; - -export const initialState = { - station: undefined, - user: { - lon: 0, - lat: 0, - }, - submitting: false, - editting: false, - isCommunity: false, - nodesData: {}, -}; - -export const reducer = (state = initialState, { type, payload }) => { - switch (type) { - case LOCATION_CHANGE: - return Object.assign({}, state, { - submitting: true, - editting: false, - }); - - case LOCATION_CHANGE_SUCCESS: - return Object.assign({}, state, { submitting: false }); - - case LOCATION_LOAD_SUCCESS: - return Object.assign({}, state, { - station: payload.location || payload, - isCommunity: payload.default || false, - }); - - case LOCATION_LOAD_LINKS_SUCCESS: - return Object.assign({}, state, { nodesData: payload }); - - case LOCATION_USER_SET: - return Object.assign({}, state, { user: payload }); - - case LOCATION_TOOGLE_EDIT: - return Object.assign({}, state, { editting: payload }); - - default: - return state; - } -}; diff --git a/plugins/lime-plugin-locate/src/locateSelectors.js b/plugins/lime-plugin-locate/src/locateSelectors.js deleted file mode 100755 index d6105732..00000000 --- a/plugins/lime-plugin-locate/src/locateSelectors.js +++ /dev/null @@ -1,17 +0,0 @@ -const getCoordinate = (coord) => (state) => { - // Not yet retrieved from the node. - if (typeof state.locate.station === "undefined") { - return undefined; - } - // Neither community nor node has location configured - const latlong = state.locate.station; - if (latlong[coord] === "FIXME") { - return null; - } - return latlong[coord]; -}; - -export const getLat = getCoordinate("lat"); -export const getLon = getCoordinate("lon"); -export const getUserLocation = (state) => state.locate.user; -export const isCommunityLocation = (state) => state.locate.isCommunity; diff --git a/plugins/lime-plugin-locate/src/style.less b/plugins/lime-plugin-locate/src/style.less index 6f11917a..5a6fc9ec 100644 --- a/plugins/lime-plugin-locate/src/style.less +++ b/plugins/lime-plugin-locate/src/style.less @@ -14,7 +14,7 @@ top: calc(50% - 5px); } -.editAction{ +.editAction { display: flex; flex-direction: column; position: absolute; @@ -30,10 +30,11 @@ } } -.loadingContainer{ +.loadingContainer { z-index: 999; - position: relative; + position: absolute; top: calc(50% - 5px); + left: calc(50% - 35px); } .hasAssetError { diff --git a/plugins/lime-plugin-node-admin/src/components/config/config.js b/plugins/lime-plugin-node-admin/src/components/config/config.js new file mode 100644 index 00000000..f72bbd5d --- /dev/null +++ b/plugins/lime-plugin-node-admin/src/components/config/config.js @@ -0,0 +1,33 @@ +import { ListItem } from "components/list"; +import Loading from "components/loading"; + +import style from "./config.style.less"; + +export const Config = ({ + title, + subtitle, + value, + onClick, + isLoading, + ...props +}) => { + return ( + +
+
+
{title}
+ {isLoading && } + {!isLoading && ( +
+
{subtitle}
+
{value}
+
+ )} +
+
+
+
+
+
+ ); +}; diff --git a/plugins/lime-plugin-node-admin/src/components/config/config.tsx b/plugins/lime-plugin-node-admin/src/components/config/config.tsx deleted file mode 100644 index f3c4792e..00000000 --- a/plugins/lime-plugin-node-admin/src/components/config/config.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { ComponentChildren } from "preact"; - -import { ListItem } from "components/list"; -import Loading from "components/loading"; - -import style from "./config.style.less"; - -type ConfigProps = { - title: ComponentChildren; - subtitle?: ComponentChildren; - value: ComponentChildren; - onClick: () => void; - isLoading: boolean; -}; - -export const Config = ({ - title, - subtitle, - value, - onClick, - isLoading, - ...props -}: ConfigProps) => { - return ( - -
-
-
{title}
- {isLoading && } - {!isLoading && ( -
- {subtitle && ( -
- {" "} - {subtitle}{" "} -
- )} -
{value}
-
- )} -
-
-
-
-
-
- ); -}; diff --git a/plugins/lime-plugin-node-admin/src/nodeAdminPage.js b/plugins/lime-plugin-node-admin/src/nodeAdminPage.js index 656a468b..be563b37 100644 --- a/plugins/lime-plugin-node-admin/src/nodeAdminPage.js +++ b/plugins/lime-plugin-node-admin/src/nodeAdminPage.js @@ -15,6 +15,7 @@ const Hostname = () => { const { data: boardData, isLoading } = useBoardData(); const hostname = boardData && boardData.hostname; return ( + // @ts-ignore Node Name} value={hostname} @@ -28,6 +29,7 @@ const ApPassword = () => { const { data: wifiData, isLoading } = useWifiData(); const has_password = wifiData && wifiData.node_ap.has_password; return ( + // @ts-ignore Wifi Password} value={has_password ? "********" : No password} @@ -63,6 +65,7 @@ export const Hotspot = () => { const { data, isLoading } = useHotspotData(); const enabled = data?.enabled; return ( + // @ts-ignore Connect to a Mobile Hotspot} diff --git a/plugins/lime-plugin-pirania/nodeAdmin/PortalConfigItem.tsx b/plugins/lime-plugin-pirania/nodeAdmin/PortalConfigItem.tsx index 977da407..1cb42c7b 100644 --- a/plugins/lime-plugin-pirania/nodeAdmin/PortalConfigItem.tsx +++ b/plugins/lime-plugin-pirania/nodeAdmin/PortalConfigItem.tsx @@ -21,6 +21,7 @@ export const PortalConfigItem = () => { } } return ( + // @ts-ignore Community Portal} diff --git a/tsconfig.json b/tsconfig.json index db4284e0..32e04bb0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,34 +1,34 @@ { "compilerOptions": { - /* Basic Options */ - "target": "ES5", - "allowJs": true, - "checkJs": true, + /* Basic Options */ + "target": "ES5", + "allowJs": true, + "checkJs": true, "jsx": "preserve", "jsxFactory": "h", "jsxFragmentFactory": "Fragment", - "jsxImportSource": "preact", - "noEmit": true, + "jsxImportSource": "preact", + "noEmit": true, - /* Strict Type-Checking Options */ - "strict": false, + /* Strict Type-Checking Options */ + "strict": false, - /* Module Resolution Options */ - "moduleResolution": "node", - "esModuleInterop": true, - "baseUrl": "./", - "paths": { - "react/*": ["./node_modules/preact/compat/*"], - "react-dom/*": ["./node_modules/preact/compat/*"], - "components/*": ["./src/components/*"], - "containers/*": ["./src/containers/*"], - "utils/*": ["./src/utils/*"], - "plugins/*": ["./plugins/*"] - }, - "allowSyntheticDefaultImports": true, + /* Module Resolution Options */ + "moduleResolution": "node", + "esModuleInterop": true, + "baseUrl": ".", + "paths": { + "react": ["node_modules/preact/compat"], + "react-dom": ["node_modules/preact/compat"], + "components/*": ["./src/components/*"], + "containers/*": ["./src/containers/*"], + "utils/*": ["./src/utils/*"], + "plugins/*": ["./plugins/*"] + }, + "allowSyntheticDefaultImports": true, - /* Advanced Options */ - "skipLibCheck": true + /* Advanced Options */ + "skipLibCheck": true }, "include": ["plugins/**/*", "src/**/*", "stories/**/*"], "exclude": [