Skip to content

Commit

Permalink
Merge pull request #1121 from geoadmin/feat-pb-580-add-warning-leavin…
Browse files Browse the repository at this point in the history
…g-drawing-mode

PB-580: Add warning when drawing could get lost otherwise
  • Loading branch information
sommerfe authored Jan 16, 2025
2 parents d38cd2f + ba153c7 commit 27d993a
Show file tree
Hide file tree
Showing 16 changed files with 383 additions and 16 deletions.
15 changes: 12 additions & 3 deletions src/modules/drawing/components/DrawingHeader.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
<script setup>
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
import { computed, ref } from 'vue'
import { computed, ref, toRefs } from 'vue'
import { useI18n } from 'vue-i18n'
import { useStore } from 'vuex'
import TextTruncate from '@/utils/components/TextTruncate.vue'
const props = defineProps({
isClosingInToolbox: {
type: Boolean,
default: false,
},
})
const { isClosingInToolbox } = toRefs(props)
const emits = defineEmits(['close'])
const store = useStore()
const isClosing = ref(false)
const isClosingWithWarning = computed(() => isClosing.value && isClosingInToolbox.value)
const drawingTitle = computed(() => store.state.drawing.drawingOverlay.title)
const i18n = useI18n()
Expand All @@ -24,12 +33,12 @@ function onClose() {
<div class="drawing-header d-flex justify-content-between align-items-center">
<button
class="drawing-header-close-button btn btn-dark"
:disabled="isClosing"
:disabled="isClosingWithWarning"
data-cy="drawing-header-close-button"
@click="onClose"
>
<FontAwesomeIcon class="icon me-2" :icon="['fas', 'arrow-left']" />
<span v-if="isClosing">
<span v-if="isClosingWithWarning">
{{ i18n.t('draw_file_saving') }}
</span>
<span v-else>
Expand Down
36 changes: 34 additions & 2 deletions src/modules/drawing/components/DrawingToolbox.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import DrawingExporter from '@/modules/drawing/components/DrawingExporter.vue'
import DrawingHeader from '@/modules/drawing/components/DrawingHeader.vue'
import DrawingToolboxButton from '@/modules/drawing/components/DrawingToolboxButton.vue'
import SharePopup from '@/modules/drawing/components/SharePopup.vue'
import ShareWarningPopup from '@/modules/drawing/components/ShareWarningPopup.vue'
import { DrawingState } from '@/modules/drawing/lib/export-utils'
import useSaveKmlOnChange from '@/modules/drawing/useKmlDataManagement.composable'
import { EditMode } from '@/store/modules/drawing.store'
Expand All @@ -29,7 +30,9 @@ const emits = defineEmits(['removeLastPoint', 'closeDrawing'])
const drawMenuOpen = ref(true)
const showClearConfirmationModal = ref(false)
const showShareModal = ref(false)
const showNotSharedDrawingWarningModal = ref(false)
const showNotSharedDrawingWarning = computed(() => store.getters.showNotSharedDrawingWarning)
const isClosingDrawing = ref(false)
const isDesktopMode = computed(() => store.getters.isDesktopMode)
const isPhoneMode = computed(() => store.getters.isPhoneMode)
const isDrawingEmpty = computed(() => store.getters.isDrawingEmpty)
Expand Down Expand Up @@ -92,6 +95,8 @@ function onCloseClearConfirmation(confirmed) {
if (confirmed) {
store.dispatch('clearDrawingFeatures', dispatcher)
store.dispatch('clearAllSelectedFeatures', dispatcher)
store.dispatch('setIsDrawingModified', { value: false, ...dispatcher })
store.dispatch('setIsDrawingEditShared', { value: false, ...dispatcher })
drawingLayer.getSource().clear()
debounceSaveDrawing()
store.dispatch('setDrawingMode', { mode: null, ...dispatcher })
Expand All @@ -115,9 +120,24 @@ watch(activeKmlLayer, () => {
})
function closeDrawing() {
isClosingDrawing.value = true
if (showNotSharedDrawingWarning.value) {
showNotSharedDrawingWarningModal.value = true
} else {
emits('closeDrawing')
}
}
function onAcceptWarningModal() {
showNotSharedDrawingWarningModal.value = false
emits('closeDrawing')
}
function onCloseWarningModal() {
isClosingDrawing.value = false
showNotSharedDrawingWarningModal.value = false
}
function selectDrawingMode(drawingMode) {
store.dispatch('setDrawingMode', { mode: drawingMode, ...dispatcher })
}
Expand All @@ -140,7 +160,11 @@ const debounceSaveDrawingName = debounce(async (newName) => {
<template>
<teleport to=".drawing-toolbox-in-menu">
<DrawingHeader v-if="isDesktopMode" @close="closeDrawing" />
<DrawingHeader
v-if="isDesktopMode"
:is-closing-in-toolbox="isClosingDrawing"
@close="closeDrawing"
/>
<div :class="[{ 'drawing-toolbox-closed': !drawMenuOpen }, 'drawing-toolbox']">
<div
class="card text-center drawing-toolbox-content shadow-lg rounded-bottom rounded-top-0 rounded-start-0"
Expand Down Expand Up @@ -287,6 +311,14 @@ const debounceSaveDrawingName = debounce(async (newName) => {
>
<SharePopup :kml-layer="activeKmlLayer" />
</ModalWithBackdrop>
<ModalWithBackdrop
v-if="showNotSharedDrawingWarningModal"
fluid
:title="$t('warning')"
@close="onCloseWarningModal()"
>
<ShareWarningPopup :kml-layer="activeKmlLayer" @accept="onAcceptWarningModal()" />
</ModalWithBackdrop>
</teleport>
</template>
Expand Down
9 changes: 9 additions & 0 deletions src/modules/drawing/components/SharePopup.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<script setup>
import { computed, onUnmounted, ref, toRefs, watch } from 'vue'
import { useStore } from 'vuex'
import KMLLayer from '@/api/layers/KMLLayer.class'
import { createShortLink } from '@/api/shortlink.api'
import router from '@/router'
import { encodeLayerId } from '@/router/storeSync/layersParamParser'
import log from '@/utils/logging'
const dispatcher = { dispatcher: 'SharePopup.vue' }
const store = useStore()
const props = defineProps({
kmlLayer: {
type: KMLLayer,
Expand Down Expand Up @@ -71,6 +76,10 @@ async function copyAdminShareUrl() {
try {
await navigator.clipboard.writeText(adminShareUrl.value)
adminUrlCopied.value = true
store.dispatch('setIsDrawingEditShared', {
value: true,
...dispatcher,
})
adminTimeout = setTimeout(() => {
adminUrlCopied.value = false
}, 5000)
Expand Down
152 changes: 152 additions & 0 deletions src/modules/drawing/components/ShareWarningPopup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
<script setup>
import { computed, onUnmounted, ref, toRefs, watch } from 'vue'
import { useStore } from 'vuex'
import KMLLayer from '@/api/layers/KMLLayer.class'
import { createShortLink } from '@/api/shortlink.api'
import router from '@/router'
import { encodeLayerId } from '@/router/storeSync/layersParamParser'
import log from '@/utils/logging'
const dispatcher = { dispatcher: 'SharePopup.vue' }
const store = useStore()
const props = defineProps({
kmlLayer: {
type: KMLLayer,
default: null,
},
})
const { kmlLayer } = toRefs(props)
const emits = defineEmits(['accept'])
const adminUrlCopied = ref(false)
const shareUrl = ref(' ')
const adminShareUrl = ref(' ')
const baseUrl = computed(() => {
return `${location.origin}/#`
})
const fileUrl = computed(() => {
return `${baseUrl.value}${router.currentRoute.value.fullPath}`
})
const adminUrl = computed(() => {
if (fileUrl.value && kmlLayer.value?.adminId) {
const encodedLayerID = encodeURI(encodeLayerId(kmlLayer.value))
return fileUrl.value.replace(
encodedLayerID,
`${encodedLayerID}@adminId=${kmlLayer.value.adminId}`
)
}
// if no adminID is available don't show the edit share link.
return null
})
watch(adminUrl, () => {
updateAdminShareUrl()
})
watch(fileUrl, () => {
updateShareUrl()
})
updateShareUrl()
updateAdminShareUrl()
let adminTimeout = null
let fileTimeout = null
onUnmounted(() => {
clearTimeout(adminTimeout)
clearTimeout(fileTimeout)
})
function onAccept() {
emits('accept')
}
async function copyAdminShareUrl() {
try {
await navigator.clipboard.writeText(adminShareUrl.value)
adminUrlCopied.value = true
store.dispatch('setIsDrawingEditShared', {
value: true,
...dispatcher,
})
adminTimeout = setTimeout(() => {
adminUrlCopied.value = false
}, 5000)
} catch (error) {
log.error(`Failed to copy: `, error)
}
}
async function updateShareUrl() {
if (fileUrl.value) {
try {
shareUrl.value = await createShortLink(fileUrl.value, shareUrl.value)
} catch (error) {
// Fallback to normal url
shareUrl.value = fileUrl.value
}
}
}
async function updateAdminShareUrl() {
if (adminUrl.value) {
try {
adminShareUrl.value = await createShortLink(adminUrl.value)
} catch (error) {
// Fallback to normal url
adminShareUrl.value = adminUrl.value
}
}
}
</script>
<template>
<div class="ga-share">
<p data-cy="drawing-not-shared-admin-warning">
{{ $t('drawing_not_shared_admin_warning') }}
</p>
<div v-if="adminUrl" class="form-group">
<label>{{ $t('draw_share_admin_link') }}:</label>
<div class="input-group input-group mb-3 share-link-input">
<input
type="text"
class="form-control"
:value="adminShareUrl"
readonly
@focus="$event.target.select()"
@click="copyAdminShareUrl()"
/>
<button
class="btn btn-outline-group"
type="button"
data-cy="drawing-share-admin-link"
@click="copyAdminShareUrl()"
>
{{ adminUrlCopied ? $t('copy_success') : $t('copy_url') }}
</button>
</div>
</div>
<button data-cy="drawing-share-admin-close" class="btn btn-dark" @click="onAccept()">
{{ $t('close') }}
</button>
</div>
</template>
<style lang="scss" scoped>
@import '@/scss/media-query.mixin';
.ga-share {
width: 100%;
text-align: start;
@include respond-above(phone) {
// Do not apply this minimal width on phone otherwise we will have
// a scroll bar on phone. On Desktop it looks a bit better to increase
// the size of the input.
.share-link-input {
min-width: 30rem;
}
}
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ export default function useDrawingModeInteraction({
drawnFeature.setStyle(geoadminStyleFunction)
// see https://openlayers.org/en/latest/apidoc/module-ol_interaction_Draw-Draw.html#finishDrawing
interaction.finishDrawing()
store.dispatch('setIsDrawingModified', { value: true, ...dispatcher })
store.dispatch('addDrawingFeature', { featureId: drawnFeature.getId(), ...dispatcher })
store.dispatch('setDrawingMode', { mode: null, ...dispatcher })
if (drawEndCallback) {
Expand Down
1 change: 1 addition & 0 deletions src/modules/i18n/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"draw_type_marker": "Linie / Fläche",
"drawing_attached": "Zeichnung als Anhang hinzugefügt",
"drawing_empty_cannot_edit_name": "Bitte fügen Sie der Zeichnung etwas hinzu, bevor Sie ihren Namen bearbeiten.",
"drawing_not_shared_admin_warning": "Achtung, Sie haben den Link, mit dem Sie Ihre Zeichnung später bearbeiten können, nicht kopiert/gespeichert. Sie verlieren die Möglichkeit, Ihre Zeichnung später bearbeiten zu können, wenn Sie die Seite neu laden oder schließen.",
"drawing_too_large": "Ihre Zeichnung ist zu gross, entfernen Sie einige Details.",
"drop_invalid_url": "URL ist ungültig.",
"drop_me_here": "Datei hier ablegen (KML, KMZ, GPX, COG)",
Expand Down
1 change: 1 addition & 0 deletions src/modules/i18n/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"draw_type_marker": "Line / surface",
"drawing_attached": "Drawing added as attachment",
"drawing_empty_cannot_edit_name": "Please add something to the drawing before editing its name.",
"drawing_not_shared_admin_warning": "Warning, you have not copied/saved the link enabling you to edit your drawing at a later date. You risk not being able to edit your drawing if you reload or close the page.",
"drawing_too_large": "Your drawing is too large, remove some features",
"drop_invalid_url": "URL is not valid.",
"drop_me_here": "Drop file here (KML, KMZ, GPX, COG)",
Expand Down
1 change: 1 addition & 0 deletions src/modules/i18n/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"draw_type_marker": "Trait / surface",
"drawing_attached": "Dessin ajouté en pièce jointe",
"drawing_empty_cannot_edit_name": "Veuillez ajouter quelque chose au dessin avant de modifier son nom.",
"drawing_not_shared_admin_warning": "Attention, vous n'avez pas copié/sauvegardé le lien vous permettant d'éditer votre dessin ultérieurement. Vous risquez de perdre la possibilité d'éditer votre dessin si vous rechargez ou fermez la page.",
"drawing_too_large": "Ton dessin est trop grand, enlève quelques éléments",
"drop_invalid_url": "URL non valide.",
"drop_me_here": "Déposer le fichier ici (KML, KMZ, GPX, COG)",
Expand Down
1 change: 1 addition & 0 deletions src/modules/i18n/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"draw_type_marker": "Linea / superficie",
"drawing_attached": "Disegno aggiunto come allegato",
"drawing_empty_cannot_edit_name": "Aggiungere qualcosa al disegno prima di modificarne il nome.",
"drawing_not_shared_admin_warning": "Attenzione, non avete copiato/salvato il link che vi consente di modificare il vostro disegno più tardi. Se ricarica o chiude la pagina, rischia di perdere la possibilità di modificare il disegno ulteriormente.",
"drawing_too_large": "Il suo disegno è troppo grande, rimuova alcuni elementi",
"drop_invalid_url": "URL non valido",
"drop_me_here": "Lasciare qui il file (KML, KMZ, GPX, COG)",
Expand Down
1 change: 1 addition & 0 deletions src/modules/i18n/locales/rm.json
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@
"draw_type_marker": "Lingia / surfatscha",
"drawing_attached": "Dissegn è agiuntà sco agiunta.",
"drawing_empty_cannot_edit_name": "Bitte fügen Sie der Zeichnung etwas hinzu, bevor Sie ihren Namen bearbeiten.",
"drawing_not_shared_admin_warning": "Attenziun, Vus n'avais betg copià/salvà il link che permetta d'elavurar pli tard Voss dissegn. Vus ristgais da betg pudair elavurar Voss dissegn, sche Vus chargiais sin la pagina u serrais quella.",
"drawing_too_large": "Tes dissegn è memia grond, stizza intgins detagls",
"drop_invalid_url": "URL è nunvalid",
"drop_me_here": "Dar giu la datoteca (KML, KMZ, GPX, COG)",
Expand Down
7 changes: 7 additions & 0 deletions src/router/storeSync/LayerParamConfig.class.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ function dispatchLayersFromUrlIntoStore(to, store, urlParamValue) {
dispatcher: STORE_DISPATCHER_ROUTER_PLUGIN,
})
)

promisesForAllDispatch.push(
store.dispatch('setIsVisitWithAdminId', {
value: true,
dispatcher: STORE_DISPATCHER_ROUTER_PLUGIN,
})
)
}
log.debug(` Add layer ${parsedLayer.id} to active layers`, layerObject)
}
Expand Down
Loading

0 comments on commit 27d993a

Please sign in to comment.