@@ -7,6 +7,9 @@ import tArea from "@turf/area";
7
7
import tBboxPolygon from "@turf/bbox-polygon" ;
8
8
import { getFgbData } from "../utils/get-fgb-data.ts" ;
9
9
import { Map } from "maplibre-gl" ;
10
+ import { useEffect } from "preact/hooks" ;
11
+
12
+ // eslint-disable-next-line no-duplicate-imports
10
13
import type { Dispatch , Reducer } from "preact/hooks" ;
11
14
12
15
const availableTimestamps = [
@@ -33,6 +36,15 @@ export enum AppActionTypes {
33
36
UPDATE_VIEW_ERROR = "UPDATE_VIEW_ERROR" ,
34
37
}
35
38
39
+ // Utility function to update URL params
40
+ const updateURLParams = ( params : Record < string , string | number | boolean > ) => {
41
+ const url = new URL ( window . location . href ) ;
42
+ Object . keys ( params ) . forEach ( ( key ) =>
43
+ url . searchParams . set ( key , String ( params [ key ] ) ) ,
44
+ ) ;
45
+ window . history . pushState ( { } , "" , url . href ) ;
46
+ } ;
47
+
36
48
export type AppReducer < State , Action > = ( state : State , action : Action ) => State ;
37
49
/* eslint-enable no-unused-vars */
38
50
@@ -44,17 +56,35 @@ export interface AppState {
44
56
timestamps : string [ ] ;
45
57
}
46
58
47
- const appInitialState : AppState = {
48
- map : undefined ,
49
- mapStatus : MapStatus . IDLE ,
50
- mapData : {
51
- type : "FeatureCollection" ,
52
- features : [ ] ,
53
- } ,
54
- currentTimestamp : new Date ( availableTimestamps [ 2 ] ) ,
55
- timestamps : [ ...availableTimestamps ] ,
59
+ const getInitialStateFromURL = ( ) => {
60
+ const urlParams = new URLSearchParams ( window . location . search ) ;
61
+
62
+ const lng = urlParams . get ( "lng" ) ;
63
+ const lat = urlParams . get ( "lat" ) ;
64
+ const zoom = urlParams . get ( "zoom" ) ;
65
+ const timestamp = urlParams . get ( "timestamp" ) ;
66
+
67
+ return {
68
+ map : undefined ,
69
+ mapStatus : MapStatus . IDLE ,
70
+ mapData : {
71
+ type : "FeatureCollection" as const ,
72
+ features : [ ] ,
73
+ } ,
74
+ currentTimestamp : timestamp
75
+ ? new Date ( timestamp )
76
+ : new Date ( availableTimestamps [ 2 ] ) ,
77
+ timestamps : [ ...availableTimestamps ] ,
78
+ initialView : {
79
+ lng : lng ? parseFloat ( lng ) : undefined ,
80
+ lat : lat ? parseFloat ( lat ) : undefined ,
81
+ zoom : zoom ? parseFloat ( zoom ) : undefined ,
82
+ } ,
83
+ } ;
56
84
} ;
57
85
86
+ const appInitialState : AppState = getInitialStateFromURL ( ) ;
87
+
58
88
export type AppAction =
59
89
| {
60
90
type : AppActionTypes . SET_MAP_REF ;
@@ -110,6 +140,16 @@ function appReducer(state: AppState, action: AppAction) {
110
140
const area = tArea ( poly ) ;
111
141
const formattedArea = new Intl . NumberFormat ( ) . format ( area / 1e6 ) ;
112
142
143
+ // Update URL params
144
+ const center = state . map . getCenter ( ) ;
145
+ const zoom = state . map . getZoom ( ) ;
146
+ updateURLParams ( {
147
+ lng : center . lng ,
148
+ lat : center . lat ,
149
+ zoom,
150
+ timestamp : currentTimestamp . toISOString ( ) ,
151
+ } ) ;
152
+
113
153
return {
114
154
...state ,
115
155
formattedArea,
@@ -188,9 +228,21 @@ const asyncActionHandlers: AsyncActionHandlers<
188
228
} ;
189
229
190
230
export const useAppReducer = ( ) => {
191
- return useReducerAsync (
231
+ const [ state , dispatch ] = useReducerAsync (
192
232
logReducer ( appReducer ) ,
193
233
appInitialState ,
194
234
asyncActionHandlers ,
195
235
) ;
236
+
237
+ useEffect ( ( ) => {
238
+ if ( state . map && state . initialView ) {
239
+ const { lng, lat, zoom } = state . initialView ;
240
+ if ( lng !== undefined && lat !== undefined && zoom !== undefined ) {
241
+ state . map . setCenter ( [ lng , lat ] ) ;
242
+ state . map . setZoom ( zoom ) ;
243
+ }
244
+ }
245
+ } , [ state . initialView , state . map ] ) ;
246
+
247
+ return [ state , dispatch ] ;
196
248
} ;
0 commit comments