diff --git a/src/api.js b/src/api.js index 23648d0..ade6c85 100644 --- a/src/api.js +++ b/src/api.js @@ -7,17 +7,24 @@ import { getApiUrl, getLocationHistoryCount } from "@/util"; * * @param {String} path API resource path * @param {Object} [params] Query parameters - * @returns {Promise} Promise returned by the fetch function + * @param {Object} [fetchOptions] + * fetch() options (merged with config.api.fetchOptions) + * @returns {Promise} Response returned by the fetch call */ -const fetchApi = (path, params = {}) => { +const fetchApi = (path, params = {}, fetchOptions = {}) => { const url = getApiUrl(path); - Object.keys(params).forEach((key) => - url.searchParams.append(key, params[key]) - ); + Object.keys(params).forEach((key) => url.searchParams.set(key, params[key])); log("HTTP", `GET ${url.href}`); - return fetch(url.href, config.api.fetchOptions).catch((error) => - log("HTTP", error, logLevels.ERROR) - ); + return fetch(url.href, { + ...fetchOptions, + ...config.api.fetchOptions, + }).catch((error) => { + if (error.name === "AbortError") { + log("HTTP", `GET ${url.href} - Request was aborted`, logLevels.WARNING); + } else { + log("HTTP", error, logLevels.ERROR); + } + }); }; /** @@ -107,21 +114,27 @@ export const getLastLocations = async (user, device) => { * @param {Device} device Device name * @param {String} start Start date and time in UTC * @param {String} end End date and time in UTC + * @param {Object} [fetchOptions] fetch() options * @returns {Promise} Array of location history objects */ export const getUserDeviceLocationHistory = async ( user, device, start, - end + end, + fetchOptions ) => { - const response = await fetchApi("/api/0/locations", { - from: start, - to: end, - user, - device, - format: "json", - }); + const response = await fetchApi( + "/api/0/locations", + { + from: start, + to: end, + user, + device, + format: "json", + }, + fetchOptions + ); const json = await response.json(); const userDeviceLocationHistory = json.data; log( @@ -141,9 +154,10 @@ export const getUserDeviceLocationHistory = async ( * Devices of which the history should be fetched * @param {String} start Start date and time in UTC * @param {String} end End date and time in UTC + * @param {Object} [fetchOptions] fetch() options * @returns {Promise} Location history */ -export const getLocationHistory = async (devices, start, end) => { +export const getLocationHistory = async (devices, start, end, fetchOptions) => { const locationHistory = {}; await Promise.all( Object.keys(devices).map(async (user) => { @@ -154,7 +168,8 @@ export const getLocationHistory = async (devices, start, end) => { user, device, start, - end + end, + fetchOptions ); }) ); diff --git a/src/components/modals/Loading.vue b/src/components/modals/Loading.vue index 0cdf604..1bc9a36 100644 --- a/src/components/modals/Loading.vue +++ b/src/components/modals/Loading.vue @@ -1,16 +1,29 @@ diff --git a/src/locales/de-DE.json b/src/locales/de-DE.json index f338336..4e63dc5 100644 --- a/src/locales/de-DE.json +++ b/src/locales/de-DE.json @@ -26,6 +26,7 @@ "OwnTracks documentation": "OwnTracks Dokumentation", "OwnTracks on Twitter": "OwnTracks auf Twitter", "Loading data, please wait...": "Daten werden geladen, bitte warten...", + "Cancel": "Abbrechen", "Image of {deviceName}": "Bild von {deviceName}", "Timestamp": "Zeitstempel", "Location": "Standort", diff --git a/src/locales/en-GB.json b/src/locales/en-GB.json index 8bd0b2e..586481b 100644 --- a/src/locales/en-GB.json +++ b/src/locales/en-GB.json @@ -26,6 +26,7 @@ "OwnTracks documentation": "OwnTracks documentation", "OwnTracks on Twitter": "OwnTracks on Twitter", "Loading data, please wait...": "Loading data, please wait...", + "Cancel": "Cancel", "Image of {deviceName}": "Image of {deviceName}", "Timestamp": "Timestamp", "Location": "Location", diff --git a/src/locales/en-US.json b/src/locales/en-US.json index 4c6da67..275c542 100644 --- a/src/locales/en-US.json +++ b/src/locales/en-US.json @@ -26,6 +26,7 @@ "OwnTracks documentation": "OwnTracks documentation", "OwnTracks on Twitter": "OwnTracks on Twitter", "Loading data, please wait...": "Loading data, please wait...", + "Cancel": "Cancel", "Image of {deviceName}": "Image of {deviceName}", "Timestamp": "Timestamp", "Location": "Location", diff --git a/src/locales/es-ES.json b/src/locales/es-ES.json index 13bfdcb..40f97ef 100644 --- a/src/locales/es-ES.json +++ b/src/locales/es-ES.json @@ -26,6 +26,7 @@ "OwnTracks documentation": "OwnTracks - documentación", "OwnTracks on Twitter": "OwnTracks en Twitter", "Loading data, please wait...": "Cargando datos, por favor, espera...", + "Cancel": "Cancelar", "Image of {deviceName}": "Imágen de {deviceName}", "Timestamp": "Fecha / Hora", "Location": "Ubicación", diff --git a/src/locales/fr-FR.json b/src/locales/fr-FR.json index bca8d61..0dc047d 100644 --- a/src/locales/fr-FR.json +++ b/src/locales/fr-FR.json @@ -26,6 +26,7 @@ "OwnTracks documentation": "Documentation d'OwnTracks", "OwnTracks on Twitter": "OwnTracks sur Twitter", "Loading data, please wait...": "Chargement des données, merci de patienter ...", + "Cancel": "Annuler", "Image of {deviceName}": "Image de {deviceName}", "Timestamp": "Horodatage", "Location": "Localisation", diff --git a/src/store/actions.js b/src/store/actions.js index f63778b..60ef3bb 100644 --- a/src/store/actions.js +++ b/src/store/actions.js @@ -184,12 +184,21 @@ const getLocationHistory = async ({ commit, state }) => { } else { devices = state.devices; } - const locationHistory = await api.getLocationHistory( - devices, - state.startDateTime, - state.endDateTime - ); - commit(types.SET_IS_LOADING, false); + commit(types.SET_REQUEST_ABORT_CONTROLLER, new AbortController()); + let locationHistory; + try { + locationHistory = await api.getLocationHistory( + devices, + state.startDateTime, + state.endDateTime, + { signal: state.requestAbortController.signal } + ); + } catch (error) { + return; + } finally { + commit(types.SET_REQUEST_ABORT_CONTROLLER, null); + commit(types.SET_IS_LOADING, false); + } commit(types.SET_LOCATION_HISTORY, locationHistory); if (config.showDistanceTravelled) { const { distanceTravelled, elevationGain, elevationLoss } = _getTravelStats( diff --git a/src/store/index.js b/src/store/index.js index 4e63bc1..74bf8be 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -33,6 +33,7 @@ export default new Vuex.Store({ distanceTravelled: 0, elevationGain: 0, elevationLoss: 0, + requestAbortController: null, }, getters, mutations, diff --git a/src/store/mutation-types.js b/src/store/mutation-types.js index b51a75b..49fbace 100644 --- a/src/store/mutation-types.js +++ b/src/store/mutation-types.js @@ -14,3 +14,4 @@ export const SET_MAP_LAYER_VISIBILITY = "SET_MAP_LAYER_VISIBILITY"; export const SET_DISTANCE_TRAVELLED = "SET_DISTANCE_TRAVELLED"; export const SET_ELEVATION_GAIN = "SET_ELEVATION_GAIN"; export const SET_ELEVATION_LOSS = "SET_ELEVATION_LOSS"; +export const SET_REQUEST_ABORT_CONTROLLER = "SET_REQUEST_ABORT_CONTROLLER"; diff --git a/src/store/mutations.js b/src/store/mutations.js index 3c09c58..cc4daf4 100644 --- a/src/store/mutations.js +++ b/src/store/mutations.js @@ -49,4 +49,7 @@ export default { [types.SET_ELEVATION_LOSS](state, elevationLoss) { state.elevationLoss = elevationLoss; }, + [types.SET_REQUEST_ABORT_CONTROLLER](state, requestAbortController) { + state.requestAbortController = requestAbortController; + }, };