Skip to content

Commit

Permalink
Merge pull request #950 from geoadmin/bug-geoloc
Browse files Browse the repository at this point in the history
Improve geolocation error management by removing the alert() - #patch
  • Loading branch information
ltshb authored Jun 24, 2024
2 parents 69085fa + 85a12c6 commit 3ff6c13
Show file tree
Hide file tree
Showing 6 changed files with 169 additions and 18 deletions.
11 changes: 10 additions & 1 deletion src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* Will listen for screen size changes and commit this changes to the store
*/
import { onMounted, onUnmounted, ref } from 'vue'
import { computed, onMounted, onUnmounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import ErrorWindow from '@/utils/components/ErrorWindow.vue'
import debounce from '@/utils/debounce'
const withOutline = ref(false)
Expand All @@ -20,6 +21,8 @@ const dispatcher = { dispatcher: 'App.vue' }
let debouncedOnResize
const errorText = computed(() => store.state.ui.errorText)
onMounted(() => {
// reading size
setScreenSizeFromWindowSize()
Expand Down Expand Up @@ -51,6 +54,12 @@ function refreshPageTitle() {
@pointerdown="withOutline = false"
>
<router-view />
<ErrorWindow
v-if="errorText"
title="error"
@close="store.dispatch('setErrorText', { errorText: null, ...dispatcher })"
><div>{{ i18n.t(errorText) }}</div></ErrorWindow
>
</div>
</template>

Expand Down
39 changes: 26 additions & 13 deletions src/modules/map/components/toolbox/GeolocButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,29 +8,42 @@ const dispatcher = { dispatcher: 'GeolocButton.vue' }
const store = useStore()
useTippyTooltip('.geoloc-button[data-tippy-content]', { placement: 'left' })
useTippyTooltip('.geoloc-button-div[data-tippy-content]', { placement: 'left' })
const isActive = computed(() => store.state.geolocation.active)
const isDenied = computed(() => store.state.geolocation.denied)
const tippyContent = computed(() => {
if (isDenied.value) {
return 'geoloc_permission_denied'
}
if (isActive.value) {
return 'geoloc_stop_tracking'
}
return 'geoloc_start_tracking'
})
function toggleGeolocation() {
store.dispatch('toggleGeolocation', dispatcher)
}
</script>

<template>
<button
class="toolbox-button geoloc-button"
type="button"
:class="{ active: isActive, disabled: isDenied }"
:data-tippy-content="isActive ? 'geoloc_stop_tracking' : 'geoloc_start_tracking'"
data-cy="geolocation-button"
@click="toggleGeolocation"
>
<svg xmlns="http://www.w3.org/2000/svg" y="0" x="0">
<ellipse class="geoloc-button-inner-circle" />
</svg>
</button>
<!-- Here below we need to set the tippy to an external div instead of directly to the button,
otherwise the tippy won't work when the button is disabled -->
<div class="geoloc-button-div" :data-tippy-content="tippyContent">
<button
class="toolbox-button geoloc-button"
type="button"
:disabled="isDenied"
:class="{ active: isActive, disabled: isDenied }"
data-cy="geolocation-button"
@click="toggleGeolocation"
>
<svg xmlns="http://www.w3.org/2000/svg" y="0" x="0">
<ellipse class="geoloc-button-inner-circle" />
</svg>
</button>
</div>
</template>

<style lang="scss" scoped>
Expand Down
13 changes: 13 additions & 0 deletions src/store/modules/ui.store.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,15 @@ export default {
* @type Boolean
*/
showDisclaimer: true,

/**
* Text to display in the error window
*
* When this text is set the error window will be displayed
*
* @type String
*/
errorText: null,
},
getters: {
showLoadingBar(state) {
Expand Down Expand Up @@ -368,6 +377,9 @@ export default {
setShowDisclaimer({ commit }, { showDisclaimer, dispatcher }) {
commit('setShowDisclaimer', { showDisclaimer, dispatcher })
},
setErrorText({ commit }, { errorText, dispatcher }) {
commit('setErrorText', { errorText, dispatcher })
},
},
mutations: {
setSize(state, { height, width }) {
Expand Down Expand Up @@ -427,5 +439,6 @@ export default {
state.featureInfoPosition = position
},
setShowDisclaimer: (state, { showDisclaimer }) => (state.showDisclaimer = showDisclaimer),
setErrorText: (state, { errorText }) => (state.errorText = errorText),
},
}
10 changes: 6 additions & 4 deletions src/store/plugins/geolocation-management.plugin.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import proj4 from 'proj4'

import { IS_TESTING_WITH_CYPRESS } from '@/config'
import i18n from '@/modules/i18n'
import { STANDARD_ZOOM_LEVEL_1_25000_MAP } from '@/utils/coordinates/CoordinateSystem.class'
import { WGS84 } from '@/utils/coordinates/coordinateSystems'
import CustomCoordinateSystem from '@/utils/coordinates/CustomCoordinateSystem.class.js'
Expand Down Expand Up @@ -53,17 +52,20 @@ const handlePositionError = (error, store) => {
denied: true,
...dispatcher,
})
alert(i18n.global.t('geoloc_permission_denied'))
store.dispatch('setErrorText', { errorText: 'geoloc_permission_denied', ...dispatcher })
break
case error.TIMEOUT:
store.dispatch('setErrorText', { errorText: 'geoloc_time_out', ...dispatcher })
break
default:
if (IS_TESTING_WITH_CYPRESS && error.code === error.POSITION_UNAVAILABLE) {
// edge case for e2e testing, if we are testing with Cypress and we receive a POSITION_UNAVAILABLE
// we don't raise an alert as it's "normal" in Electron to have this error raised (this API doesn't work
// we don't raise an error as it's "normal" in Electron to have this error raised (this API doesn't work
// on Electron embedded in Cypress : no Geolocation hardware detected, etc...)
// the position will be returned by a mocked up function by Cypress we can ignore this error
// we do nothing...
} else {
alert(i18n.global.t('geoloc_unknown'))
store.dispatch('setErrorText', { errorText: 'geoloc_unknown', ...dispatcher })
}
}
}
Expand Down
110 changes: 110 additions & 0 deletions src/utils/components/ErrorWindow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<script setup>
import { computed, ref, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
const props = defineProps({
title: {
type: String,
default: '',
},
/**
* Hide the modal with backdrop, can be used to temporarily hide the modal without loosing its
* content
*/
hide: {
type: Boolean,
default: false,
},
/** Add a minimize button in header that will hide/show the body */
hasMinimize: {
type: Boolean,
default: true,
},
})
const { title, hide } = toRefs(props)
const store = useStore()
const showBody = ref(true)
const hasDevSiteWarning = computed(() => store.getters.hasDevSiteWarning)
const i18n = useI18n()
const emit = defineEmits(['close'])
</script>

<template>
<teleport to="#main-component">
<div
v-show="!hide"
class="simple-window card bg-danger text-white fw-bold"
:class="{ 'dev-disclaimer-present': hasDevSiteWarning }"
data-cy="error-window"
>
<div
class="card-header d-flex align-items-center justify-content-sm-end"
data-cy="window-header"
>
<span v-if="title" class="me-auto text-truncate">{{ i18n.t(title) }}</span>
<span v-else class="me-auto" />
<button
class="btn btn-light btn-sm btn-outline-danger me-2"
@click.stop="showBody = !showBody"
>
<FontAwesomeIcon :icon="`caret-${showBody ? 'down' : 'right'}`" />
</button>
<button
class="btn btn-light btn-sm btn-outline-danger"
data-cy="error-window-close"
@click.stop="emit('close')"
>
<FontAwesomeIcon icon="times" />
</button>
</div>
<div class="card-body" :class="{ hide: !showBody }" data-cy="error-window-body">
<slot />
</div>
</div>
</teleport>
</template>

<style lang="scss" scoped>
@import '@/scss/variables.module';
@import '@/scss/media-query.mixin';
.simple-window {
$top-margin: calc(2 * $header-height + 2rem);
z-index: calc($zindex-menu - 1);
position: fixed;
top: $top-margin;
right: 4rem;
width: max-content;
max-width: 400px;
max-height: calc(100vh - $top-margin);
@include respond-below(phone) {
$top-margin: $header-height;
&.dev-disclaimer-present {
$top-margin: calc($header-height + $dev-disclaimer-height);
}
top: $top-margin;
left: 50%;
right: unset;
transform: translate(-50%, 0%);
max-height: calc(100vh - $top-margin);
max-width: 100vw;
}
.card-body {
overflow-y: auto;
&.hide {
visibility: hidden;
height: 0px;
padding: 0px;
}
}
}
</style>
4 changes: 4 additions & 0 deletions tests/cypress/tests-e2e/geolocation.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ function getGeolocationButtonAndClickIt() {
cy.get(geolocationButtonSelector).should('be.visible').click()
}

// PB-701: TODO Those tests below are not working as expected, as the cypress-browser-permissions is not
// working and the geolocation is always allowed, this needs to be reworked and probably need to
// use another plugin.

describe('Geolocation cypress', () => {
context(
'Test geolocation when first time activating it',
Expand Down

0 comments on commit 3ff6c13

Please sign in to comment.