]> git.openstreetmap.org Git - nominatim-ui.git/blob - src/state/AppState.svelte.js
Rebundle latest version
[nominatim-ui.git] / src / state / AppState.svelte.js
1 import { untrack } from 'svelte';
2 import { generate_nominatim_api_url } from '../lib/api_utils.js';
3 import { identifyLinkInQuery } from '../lib/helpers.js';
4
5 const default_pagename = Nominatim_Config.Reverse_Only ? 'reverse' : 'search';
6 const pagenames = [
7   default_pagename,
8   'reverse',
9   'details',
10   'deletable',
11   'polygons',
12   'status',
13   'about'
14 ];
15
16 class AppState {
17   page = $state();
18
19   lastApiRequestURL = $state(null);
20   errorMessage = $state(null);
21   requestProgress = $state('finish');
22
23   #abortController;
24
25   constructor() {
26     this.refreshPage();
27   }
28
29   refreshPage(pagename, params) {
30     if (typeof pagename === 'undefined') {
31       pagename = window.location.pathname.replace('.html', '').replace(/^.*\//, '');
32
33       if (!pagenames.includes(pagename)) pagename = default_pagename;
34
35       params = new URLSearchParams(window.location.search);
36     } else {
37       if (!pagenames.includes(pagename)) pagename = default_pagename;
38
39       if (typeof params === 'undefined') {
40         params = new URLSearchParams();
41       }
42
43       let param_str = params.toString();
44       if (param_str) {
45         param_str = '?' + param_str;
46       }
47       let new_url = pagename + '.html' + param_str;
48
49       if (window.location.protocol.match(/^http/)) {
50         window.history.pushState([], '', new_url);
51       } else {
52         window.location.href = new_url;
53       }
54     }
55
56     if (pagename === 'search' && params.has('q')) {
57       const arrTypeAndId = identifyLinkInQuery(params.get('q'));
58       if (arrTypeAndId instanceof Array) {
59         pagename = 'details';
60         params = new URLSearchParams({osmtype: arrTypeAndId[0], osmid: arrTypeAndId[1]});
61       }
62     }
63
64     untrack(() => {
65       if (this.page?.tab !== pagename && this.#abortController) {
66         this.#abortController.abort();
67         this.#abortController = undefined;
68       }
69       this.page = { tab: pagename, params: params };
70       this.lastApiRequestURL = null;
71       this.errorMessage = null;
72     });
73   }
74
75   async fetchFromApi(endpoint_name, params, callback) {
76     const api_url = generate_nominatim_api_url(endpoint_name, params);
77
78     const mock_api_error = (new URLSearchParams(window.location.search)).get('mock_api_error');
79
80     const fetchOptions = {};
81
82     this.requestProgress = 'start';
83     if (endpoint_name !== 'status') {
84       this.lastApiRequestURL = null;
85       // avoid API requests running in parallel
86       this.#abortController?.abort();
87       this.#abortController = new AbortController();
88       fetchOptions.signal = this.#abortController.signal;
89     }
90
91     if (Nominatim_Config.Nominatim_API_Endpoint_Headers) {
92       fetchOptions.headers = Nominatim_Config.Nominatim_API_Endpoint_Headers;
93     }
94
95     try {
96       await fetch(api_url, fetchOptions)
97         .then(async (response) => {
98           if ((!((response.status >= 200 && response.status < 300) || response.status === 404))
99               || mock_api_error === 'fetch'
100           ) {
101             this.errorMessage = `Error fetching data from ${api_url} (${response.statusText})`;
102             return undefined;
103           }
104
105           // Parse JSON here instead of returning a promise so we can catch possible
106           // errors.
107           var data;
108           try {
109             if (mock_api_error === 'parse') {
110               data = JSON.parse('{');
111             } else {
112               data = await response.json();
113             }
114           } catch (err) {
115             // e.g. 'JSON.parse: unexpected non-whitespace character after JSON data at line 1'
116             this.errorMessage = `Error parsing JSON data from ${api_url} (${err})`;
117             return undefined;
118           }
119           return data;
120         })
121         .then((data) => {
122           if (data) {
123             if (data.error) {
124               this.errorMessage = data.error.message;
125             }
126             callback(data);
127           }
128           this.requestProgress = 'finish';
129         });
130     } catch (error) {
131       if (error.name !== 'AbortError') {
132         this.errorMessage = `Error fetching data from ${api_url} (${error})`;
133         this.requestProgress = 'finish';
134       }
135     }
136
137     if (endpoint_name !== 'status') this.lastApiRequestURL = api_url;
138   }
139 }
140
141 export const appState = new AppState();