Skip to content

Commit 508747a

Browse files
authored
Merge pull request #14 from itk-dev/feature/navigation-handling
Feature/navigation handling
2 parents ff4811e + 5928e98 commit 508747a

File tree

12 files changed

+808
-19315
lines changed

12 files changed

+808
-19315
lines changed

package-lock.json

Lines changed: 552 additions & 19163 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@
33
"version": "0.1.0",
44
"private": true,
55
"dependencies": {
6-
"@testing-library/jest-dom": "^5.16.5",
6+
"@testing-library/jest-dom": "^5.17.0",
77
"@testing-library/react": "^13.4.0",
88
"@testing-library/user-event": "^13.5.0",
99
"ol": "^7.4.0",
1010
"proj4": "^2.9.0",
1111
"react": "^18.2.0",
1212
"react-dom": "^18.2.0",
1313
"react-responsive-carousel": "^3.2.23",
14-
"react-router": "^6.4.3",
15-
"react-router-dom": "^6.4.2",
14+
"react-router": "^6.14.2",
15+
"react-router-dom": "^6.14.2",
1616
"web-vitals": "^2.1.4"
1717
},
1818
"scripts": {
@@ -43,20 +43,20 @@
4343
]
4444
},
4545
"devDependencies": {
46-
"@babel/eslint-parser": "^7.19.1",
46+
"@babel/eslint-parser": "^7.22.9",
4747
"@tailwindcss/line-clamp": "^0.4.4",
4848
"eslint-config-airbnb": "^19.0.4",
49-
"eslint-config-prettier": "^8.5.0",
50-
"eslint-plugin-cypress": "^2.12.1",
51-
"eslint-plugin-import": "^2.26.0",
52-
"eslint-plugin-jsx-a11y": "^6.6.1",
53-
"eslint-plugin-only-warn": "^1.0.3",
49+
"eslint-config-prettier": "^8.8.0",
50+
"eslint-plugin-cypress": "^2.13.3",
51+
"eslint-plugin-import": "^2.27.5",
52+
"eslint-plugin-jsx-a11y": "^6.7.1",
53+
"eslint-plugin-only-warn": "^1.1.0",
5454
"eslint-plugin-prettier": "^4.2.1",
55-
"eslint-plugin-react": "^7.31.10",
55+
"eslint-plugin-react": "^7.33.0",
5656
"eslint-plugin-react-hooks": "^4.6.0",
57-
"postcss": "^8.4.18",
58-
"prettier": "^2.7.1",
57+
"postcss": "^8.4.27",
58+
"prettier": "^2.8.8",
5959
"react-scripts": "5.0.1",
60-
"tailwindcss": "^3.1.8"
60+
"tailwindcss": "^3.3.3"
6161
}
6262
}

public/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
-->
2727
<title>Podwalk</title>
2828
</head>
29-
<body>
29+
<body style="background-color: rgb(39 39 42);">
3030
<noscript>You need to enable JavaScript to run this app.</noscript>
3131
<div id="root"></div>
3232
<!--

src/App.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ function App() {
116116
const { fileUrl } = useContext(ApiEndpointContext);
117117

118118
return (
119-
<div className="App h-full min-h-screen w-screen p-3 text-zinc-800 dark:text-white bg-zinc-100 dark:bg-zinc-800">
119+
<div className="App h-full min-h-screen w-screen p-3 text-zinc-800 dark:text-white bg-zinc-100 dark:bg-zinc-800 overflow-hidden touch-none">
120120
<LatLongContext.Provider value={contextLatLong}>
121121
<CacheContext.Provider value={cacheContext}>
122122
<AudioContext.Provider value={audio}>

src/components/BackButton.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function BackButton({ children }) {
1212
type="button"
1313
onClick={() => navigate(-1)}
1414
>
15-
<span className="bg-zinc-300 dark:bg-zinc-700 flex place-content-center px-3 mr-2 rounded">
15+
<span className="block bg-zinc-300 dark:bg-zinc-700 place-content-center px-3 mr-2 rounded">
1616
{children === "Afslut" ? (
1717
<Xmark className="inline w-2" />
1818
) : (

src/components/map/MapWrapper.jsx

Lines changed: 31 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ import TileWMS from "ol/source/TileWMS";
1111
import Projection from "ol/proj/Projection";
1212
import Proj4 from "proj4";
1313
import { register } from "ol/proj/proj4";
14+
import { latlngToUTM } from "../../util/helper";
1415
import ApiEndpointContext from "../../context/api-endpoint-context";
1516
import "./map-wrapper.css";
1617

1718
// Todo style this......
18-
function MapWrapper({ mapData, goToView }) {
19+
function MapWrapper({ mapData, goToView, hideMapOverlay }) {
1920
const { mapUsername, mapPassword } = useContext(ApiEndpointContext);
2021
const [map, setMap] = useState();
2122
const [vectorLayer, setVectorLayer] = useState(null);
@@ -29,10 +30,7 @@ function MapWrapper({ mapData, goToView }) {
2930
mapData.length > 0 ? mapData[0].northing : 574969.6851;
3031
const eastingInitial = mapData.length > 0 ? mapData[0].easting : 6223950.2116;
3132
const view = new View({
32-
minZoom: 6,
33-
maxZoom: 20,
3433
center: [northingInitial, eastingInitial],
35-
zoom: 6,
3634
resolutions: [
3735
1638.4, 819.2, 409.6, 204.8, 102.4, 51.2, 25.6, 12.8, 6.4, 3.2, 1.6, 0.8,
3836
0.4, 0.2, 0.1,
@@ -63,24 +61,40 @@ function MapWrapper({ mapData, goToView }) {
6361

6462
useEffect(() => {
6563
if (goToView && map) {
66-
const { easting, northing } = goToView;
64+
const { centerlatitude, centerlongitude, zoomValue } = goToView;
65+
const [destinationLat, destinationLong] = latlngToUTM(
66+
centerlatitude,
67+
centerlongitude
68+
);
6769
map.getView().animate({
68-
center: [northing, easting],
70+
center: [destinationLat, destinationLong],
71+
zoom: zoomValue,
6972
duration: 800,
7073
});
7174
}
72-
}, [goToView]);
75+
}, [goToView, map, hideMapOverlay]);
7376

7477
useEffect(() => {
7578
if (!mapData) {
7679
return;
7780
}
7881

82+
const firstIconStyle = new Style({
83+
image: new Icon({
84+
anchor: [0.5, 46],
85+
anchorXUnits: "fraction",
86+
anchorYUnits: "pixels",
87+
scale: 1.2,
88+
// Todo change icon
89+
src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAlCAMAAACEemK6AAABXFBMVEUAAAAAgP8AZv8AgP8Abf8AYP8Acf8XdP8Vav8Udv8Lb/8KcP8PbP8Pcf8Ocf8Mbv8Lb/8NcP8Nb/8Nbf8Mb/8Mbf8MbvsLbfsPb/sObvsObPwNcPwNbvwMbfwObfwNbvwNbv0Mbv0Ob/0Obf0Mbf0Mbv0Mbv0Obv0Nbv0Nbf0Nbv0Nbv0Mb/0Mbv0Mbf0Obv0Ob/0Obv0Nb/0Nbf0Nbv0Nbv0Nbv0Nbv0Mbv0Mb/1ppv4Nbvwvgv4NbvwNbvwNb/y81/8Nb/2jyf4Nbf0kfP0Nbv0Nbv2PvP45if4Nbv0dd/0Nbv0Nbv0Nbv0Nbv0Nbv0Nbv1SmP4Nbv0Nbv08iv0Nb/0Nbv0Nbv32+f8Rcf3u9f8Mbv281/4Nbv2cxP4Nbv17sP4Nbv0Nbv0Xdf1foP4Nbv0Nbv0Nbv0Nbv07if1Umf5cnf6Nu/6x0P7H3f/t9P/u9f/2+f/4+//////ZrbbCAAAAaHRSTlMABAUGBwgJCwwNFxkhIiQsLjk8PT4/QURFSElQUVRZZGZrbG58fX+Ai4yNjpGSk5SWl5iam5ydoKSmv8PExcfLztLT1NTW2Nna3Nzd3t/l5+jo7O3v8PLz8/T09ff4+Pn5+vv7+/z9/kqOLf8AAAABYktHRHNBCT3OAAABKklEQVQYGW3BhVYCQQAF0CeoWBjYid2Nio2Fiig22IGCz475/3Nk2RlY2LkXSnGbPxC5jwT8rcXI5RqIUYn2u2DRFKFVpBEZ3kfmindB6qSdF2k1t7R7rEVK4QF1wk4APdTrBgqOqHdYgAZmbO3c3J0xox4jVBaF4YPKEFYoLQvTO6UlhCmtCdMXpRCuKV0I0y+la8QoXQnTD6UoNiltCNMnpXXMUtr7E4a/V0o+tFDZ/RFCfL9RaYbjlMrl8cPzE5UTBzBBvXEAFXHqJCqRskCdeRiqk7RLVCFtjnYzMJVFmS9WDqmP+XqhOIPMtV2EjLoXWiU9sPDRahpWJSFm7ZcihydJJVmPPMNUBpHPsUpTwAkb9zkN525otNPQAa0pkpPQKxwLjjqR9Q+OaxIua1fKFgAAAABJRU5ErkJggg==",
90+
}),
91+
});
7992
const iconStyle = new Style({
8093
image: new Icon({
8194
anchor: [0.5, 46],
8295
anchorXUnits: "fraction",
8396
anchorYUnits: "pixels",
97+
scale: 0.7,
8498
// Todo change icon
8599
src: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABsAAAAlCAMAAACEemK6AAABXFBMVEUAAAAAgP8AZv8AgP8Abf8AYP8Acf8XdP8Vav8Udv8Lb/8KcP8PbP8Pcf8Ocf8Mbv8Lb/8NcP8Nb/8Nbf8Mb/8Mbf8MbvsLbfsPb/sObvsObPwNcPwNbvwMbfwObfwNbvwNbv0Mbv0Ob/0Obf0Mbf0Mbv0Mbv0Obv0Nbv0Nbf0Nbv0Nbv0Mb/0Mbv0Mbf0Obv0Ob/0Obv0Nb/0Nbf0Nbv0Nbv0Nbv0Nbv0Mbv0Mb/1ppv4Nbvwvgv4NbvwNbvwNb/y81/8Nb/2jyf4Nbf0kfP0Nbv0Nbv2PvP45if4Nbv0dd/0Nbv0Nbv0Nbv0Nbv0Nbv0Nbv1SmP4Nbv0Nbv08iv0Nb/0Nbv0Nbv32+f8Rcf3u9f8Mbv281/4Nbv2cxP4Nbv17sP4Nbv0Nbv0Xdf1foP4Nbv0Nbv0Nbv0Nbv07if1Umf5cnf6Nu/6x0P7H3f/t9P/u9f/2+f/4+//////ZrbbCAAAAaHRSTlMABAUGBwgJCwwNFxkhIiQsLjk8PT4/QURFSElQUVRZZGZrbG58fX+Ai4yNjpGSk5SWl5iam5ydoKSmv8PExcfLztLT1NTW2Nna3Nzd3t/l5+jo7O3v8PLz8/T09ff4+Pn5+vv7+/z9/kqOLf8AAAABYktHRHNBCT3OAAABKklEQVQYGW3BhVYCQQAF0CeoWBjYid2Nio2Fiig22IGCz475/3Nk2RlY2LkXSnGbPxC5jwT8rcXI5RqIUYn2u2DRFKFVpBEZ3kfmindB6qSdF2k1t7R7rEVK4QF1wk4APdTrBgqOqHdYgAZmbO3c3J0xox4jVBaF4YPKEFYoLQvTO6UlhCmtCdMXpRCuKV0I0y+la8QoXQnTD6UoNiltCNMnpXXMUtr7E4a/V0o+tFDZ/RFCfL9RaYbjlMrl8cPzE5UTBzBBvXEAFXHqJCqRskCdeRiqk7RLVCFtjnYzMJVFmS9WDqmP+XqhOIPMtV2EjLoXWiU9sPDRahpWJSFm7ZcihydJJVmPPMNUBpHPsUpTwAkb9zkN525otNPQAa0pkpPQKxwLjjqR9Q+OaxIua1fKFgAAAABJRU5ErkJggg==",
86100
}),
@@ -89,13 +103,16 @@ function MapWrapper({ mapData, goToView }) {
89103
// Define feature array and apply styling
90104
const features = [];
91105

92-
mapData.forEach(({ northing, easting, name }) => {
106+
mapData.forEach(({ northing, easting, name }, index) => {
93107
const feature = new Feature({
94108
geometry: new Point([northing, easting]),
95109
name,
96110
});
97-
98-
feature.setStyle(iconStyle);
111+
if (index === 0) {
112+
feature.setStyle(firstIconStyle);
113+
} else {
114+
feature.setStyle(iconStyle);
115+
}
99116

100117
features.push(feature);
101118
});
@@ -161,6 +178,7 @@ function MapWrapper({ mapData, goToView }) {
161178
layers,
162179
target: mapElement.current,
163180
view,
181+
controls: [],
164182
});
165183

166184
// save map and vector layer references to state
@@ -175,7 +193,9 @@ function MapWrapper({ mapData, goToView }) {
175193
<div ref={mapElement} className="map rounded-xl overflow-hidden" id="map">
176194
<div id="tooltip" className="tooltip" />
177195
</div>
178-
<div className="map-color-overlay absolute left-0 top-0 bottom-0 right-0 w-screen h-screen bg-zinc-400 dark:bg-zinc-800 opacity-70 dark:opacity-80" />
196+
{!hideMapOverlay && (
197+
<div className="map-color-overlay absolute left-0 top-0 bottom-0 right-0 w-screen h-screen bg-zinc-400 dark:bg-zinc-800 opacity-70 dark:opacity-80" />
198+
)}
179199
</div>
180200
);
181201
}

src/components/points-of-interest/PointOfInterest.jsx

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import { React, useState, useEffect, useContext } from "react";
22
import LatLongContext from "../../context/latitude-longitude-context";
3-
import { getDistanceBetweenCoordinates } from "../../util/helper";
3+
import {
4+
getDistanceBetweenCoordinates,
5+
isExperienceIdInLocalstorage,
6+
} from "../../util/helper";
47
import PodcastWrapper from "./PodcastWrapper";
58
import PermissionContext from "../../context/permission-context";
69
import AudioContext from "../../context/audio-context";
@@ -22,14 +25,16 @@ function PointOfInterest({
2225
IFrameUrl,
2326
},
2427
index,
28+
destinationChanged,
29+
nextUnlockableId,
2530
}) {
2631
const { lat, long } = useContext(LatLongContext);
2732
const { setSource } = useContext(AudioContext);
2833
const [proximity, setProximity] = useState(null);
29-
const [unlocked, setUnlocked] = useState(false);
3034
const [viewSubtitles, setViewSubtitles] = useState(false);
31-
35+
const [unlocked, setUnlocked] = useState(false);
3236
const { geolocationAvailable } = useContext(PermissionContext);
37+
3338
useEffect(() => {
3439
if (
3540
latitude &&
@@ -45,23 +50,14 @@ function PointOfInterest({
4550
longitude
4651
);
4752
setProximity(distance);
48-
setUnlocked(distance < 5); // todo magic number/get from config
49-
}
50-
}, [latitude, longitude, lat, long, geolocationAvailable]);
51-
52-
function isExperienceIdInLocalstorage() {
53-
const currentLocalStorage = localStorage.getItem("unlocked-experiences");
54-
if (currentLocalStorage) {
55-
const updateLocalStorage = JSON.parse(currentLocalStorage);
56-
if (updateLocalStorage.includes(id)) {
57-
return true;
53+
if (!unlocked && id === nextUnlockableId) {
54+
setUnlocked(distance < 51); // todo magic number/get from config
5855
}
5956
}
60-
return false;
61-
}
57+
}, [latitude, longitude, lat, long, geolocationAvailable]);
6258

6359
useEffect(() => {
64-
if (isExperienceIdInLocalstorage()) {
60+
if (isExperienceIdInLocalstorage(id)) {
6561
return;
6662
}
6763
if (unlocked) {
@@ -75,6 +71,7 @@ function PointOfInterest({
7571
"unlocked-experiences",
7672
JSON.stringify(updateLocalStorage)
7773
);
74+
destinationChanged();
7875
} else {
7976
// add new "unlocked steps"
8077
localStorage.setItem("unlocked-experiences", JSON.stringify([id]));
@@ -83,7 +80,7 @@ function PointOfInterest({
8380
}, [unlocked]);
8481

8582
useEffect(() => {
86-
if (isExperienceIdInLocalstorage()) {
83+
if (isExperienceIdInLocalstorage(id)) {
8784
setUnlocked(true);
8885
}
8986
}, []);
@@ -93,7 +90,7 @@ function PointOfInterest({
9390
className={
9491
unlocked
9592
? `relative flex items-start gap-4 p-2 bg-white dark:bg-zinc-700 rounded-lg`
96-
: `relative flex items-start gap-4 p-2 bg-white dark:bg-zinc-700 rounded-lg opacity-10`
93+
: `relative flex items-start gap-4 p-2 bg-white dark:bg-zinc-700 rounded-lg opacity-30`
9794
}
9895
>
9996
<div className="absolute -left-3 px-2 font-bold rounded-full bg-emerald-700 text-zinc-100 text-sm">

src/components/routes/RouteCarousel.jsx

Lines changed: 50 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,59 @@
1-
import { React } from "react";
1+
import { React, useState } from "react";
22
import { Carousel } from "react-responsive-carousel";
3+
import { Link } from "react-router-dom";
34
import Route from "./Route";
45
import { getIdFromApiEndpoint } from "../../util/helper";
6+
import { ReactComponent as IconCirclePlay } from "../../icons/circle-play-solid.svg";
7+
import { ReactComponent as IconMap } from "../../icons/map-solid.svg";
58
import "react-responsive-carousel/lib/styles/carousel.min.css"; // requires a loader
69

7-
function RouteCarousel({ routes, onCarouselChange }) {
10+
function RouteCarousel({
11+
routes,
12+
onCarouselChange,
13+
hideMapOverlay,
14+
setHideMapOverlay,
15+
selectedRoute,
16+
}) {
817
return (
9-
<Carousel
10-
className="rounded-lg overflow-hidden md:max-w-lg"
11-
showThumbs={false}
12-
showStatus={false}
13-
autoplay
14-
infiniteLoop
15-
showArrows
16-
swipeable
17-
useKeyboardArrows
18-
emulateTouch
19-
onChange={(index) => onCarouselChange(index)}
20-
>
21-
{routes.map((route) => {
22-
const id = getIdFromApiEndpoint(route);
23-
return <Route key={id} id={id} />;
24-
})}
25-
</Carousel>
18+
<div className="absolute flex justify-end h-56 left-0 bottom-0 right-0 rounded-lg overflow-hidden w-full">
19+
{!hideMapOverlay && (
20+
<button
21+
type="button"
22+
className="h-10 mr-3 mt-5 p-2 rounded text-zinc-100 dark:text-zinc-800 bg-zinc-800 dark:bg-zinc-100 drop-shadow"
23+
>
24+
<Link className="" to={selectedRoute && `/route/${selectedRoute.id}`}>
25+
<IconCirclePlay className="w-6 h-6" />
26+
</Link>
27+
</button>
28+
)}
29+
<button
30+
onClick={() => setHideMapOverlay(!hideMapOverlay)}
31+
type="button"
32+
className="h-10 mr-5 mt-5 p-2 rounded text-zinc-100 dark:text-zinc-800 bg-zinc-800 dark:bg-zinc-100 drop-shadow"
33+
>
34+
<IconMap className="w-6 h-6" />
35+
</button>
36+
{!hideMapOverlay && (
37+
<Carousel
38+
className="absolute left-0 bottom-0 right-0 m-5 rounded-lg overflow-hidden max-h-96 md:max-w-lg"
39+
showThumbs={false}
40+
showStatus={false}
41+
showIndicators={false}
42+
autoplay
43+
infiniteLoop
44+
showArrows
45+
swipeable
46+
useKeyboardArrows
47+
emulateTouch
48+
onChange={(index) => onCarouselChange(index)}
49+
>
50+
{routes.map((route) => {
51+
const id = getIdFromApiEndpoint(route);
52+
return <Route key={id} id={id} />;
53+
})}
54+
</Carousel>
55+
)}
56+
</div>
2657
);
2758
}
2859

0 commit comments

Comments
 (0)