-
-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
implmenetation of a coordinate picker
- Loading branch information
1 parent
12c5daa
commit 7d4834c
Showing
11 changed files
with
463 additions
and
120 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
121 changes: 121 additions & 0 deletions
121
webclient/src/components/feedback/DetailsCoordinatePicker.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
<script setup lang="ts"> | ||
import { selectedMap, useDetailsStore } from "@/stores/details"; | ||
import { Coord, useGlobalStore } from "@/stores/global"; | ||
import { ref } from "vue"; | ||
import { getLocalStorageWithExpiry, setLocalStorageWithExpiry } from "@/composables/storage"; | ||
const state = useDetailsStore(); | ||
const global = useGlobalStore(); | ||
// The coordinate picker keeps backups of the subject and body | ||
// in case someone writes a text and then after that clicks | ||
// the set coordinate button in the feedback form. | ||
// If we no backup has been made then, this would be lost after clicking confirm there. | ||
const coord_picker = ref({ | ||
backup_id: null as string | null, | ||
subject_backup: null as string | null, | ||
body_backup: null as string | null, | ||
force_reopen: false, | ||
}); | ||
const emit = defineEmits<{ | ||
(e: "openFeedbackForm", callback: EventListener): void; | ||
}>(); | ||
function addLocationPicker() { | ||
// If this is called from the feedback form using the edit coordinate | ||
// button, we temporarily save the current subject and body, so it is | ||
// not lost when being reopened | ||
if (global.feedback.open) { | ||
coord_picker.value.backup_id = state.data?.id || "undefined"; | ||
coord_picker.value.subject_backup = global.feedback.subject; | ||
coord_picker.value.body_backup = global.feedback.body; | ||
coord_picker.value.force_reopen = true; // reopen after confirm | ||
global.temporarilyCloseFeedback(); | ||
} | ||
state.map.selected = selectedMap.interactive; | ||
// Verify that there isn't already a marker (could happen if you click 'assign | ||
// a location' multiple times from the 'missing accurate location' toast) | ||
if (marker2.value === null) { | ||
// Coordinates are either taken from the entry, or if there are already | ||
// some in the localStorage use them | ||
const currentEdits = getLocalStorageWithExpiry<{ [index: string]: Coord }>("feedback-coords", {}); | ||
const { coords } = currentEdits[state.data?.id || "undefined"] || state.data; | ||
marker2.value = new Marker({ | ||
draggable: true, | ||
color: "#ff0000", | ||
}); | ||
if (coords.lat !== undefined && coords.lon !== undefined) | ||
marker2.value.setLngLat([coords.lon, coords.lat]).addTo(map.value as Map); | ||
} | ||
} | ||
function confirmLocationPicker() { | ||
// add the current edits to the feedback | ||
const currentEdits = getLocalStorageWithExpiry<{ [index: string]: Coord }>("feedback-coords", {}); | ||
const location = marker2.value?.getLngLat(); | ||
currentEdits[state.data?.id || "undefined"] = { | ||
coords: { lat: location?.lat, lon: location?.lng }, | ||
}; | ||
// save to local storage with ttl of 12h (garbage-collected on next read) | ||
setLocalStorageWithExpiry("feedback-coords", currentEdits, 12); | ||
marker2.value?.remove(); | ||
marker2.value = null; | ||
// A feedback form is only opened when this is the only (and therefore | ||
// first coordinate). If there are more coordinates we can assume | ||
// someone is doing batch edits. They can then use the send button in | ||
// the coordinate counter at the top of the page. | ||
if (Object.keys(currentEdits).length === 1 || state.coord_picker.force_reopen) { | ||
state.coord_picker.force_reopen = false; | ||
emit("openFeedbackForm", () => addLocationPicker()); | ||
} | ||
// The helptext (which says thet you can edit multiple coordinates in bulk) | ||
// is also only shown if there is one edit. | ||
if (Object.keys(currentEdits).length === 1) { | ||
document.getElementById("feedback-coordinate-picker-helptext")?.classList.remove("d-none"); | ||
} | ||
} | ||
function cancelLocationPicker() { | ||
marker2.value?.remove(); | ||
marker2.value = null; | ||
if (state.coord_picker.force_reopen) { | ||
state.coord_picker.force_reopen = false; | ||
emit("openFeedbackForm", () => addLocationPicker()); | ||
} | ||
} | ||
</script> | ||
<template> | ||
<Teleport to="maybe-coordinate-inacurate-warning-toast"> | ||
<div class="toast toast-warning" v-if="state.data?.coords.accuracy === 'building'"> | ||
{{ $t("view_view.msg.inaccurate_only_building.primary_msg") }}<br /> | ||
<i> | ||
{{ $t("view_view.msg.inaccurate_only_building.help_others_and") }} | ||
<button class="btn btn-sm" @click="addLocationPicker"> | ||
{{ $t("view_view.msg.inaccurate_only_building.btn") }} | ||
</button> | ||
</i> | ||
</div> | ||
</Teleport> | ||
|
||
<div class="toast toast-primary location-picker mb-2" v-if="marker2"> | ||
<div class="columns"> | ||
<div class="column col col-sm-12"> | ||
{{ $t("view_view.msg.correct_location.msg") }} | ||
</div> | ||
<div class="column col-auto col-sm-12 btns"> | ||
<button class="btn btn-sm" @click="cancelLocationPicker"> | ||
{{ $t("view_view.msg.correct_location.btn-cancel") }} | ||
</button> | ||
<button class="btn btn-sm" @click="confirmLocationPicker"> | ||
<i class="icon icon-check" /> | ||
{{ $t("view_view.msg.correct_location.btn-done") }} | ||
</button> | ||
</div> | ||
</div> | ||
</div> | ||
</template> | ||
<style lang="scss"></style> |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<script setup lang="ts"> | ||
import BasicFeedbackModal from "@/components/feedback/TokenBasedModal.vue"; | ||
import { useGlobalStore } from "@/stores/global"; | ||
const global = useGlobalStore(); | ||
</script> | ||
|
||
<template> | ||
<BasicFeedbackModal :data="global.propose_edits.data"> | ||
<template v-slot:modal> | ||
<div class="form-group"> | ||
<label class="form-label" for="additional_context">{{ $t("feedback.message") }}</label> | ||
<textarea | ||
class="form-input" | ||
id="additional_context" | ||
:placeholder="$t('feedback.message')" | ||
v-model="global.propose_edits.data.additional_context" | ||
rows="6" | ||
/> | ||
</div> | ||
<h2>{{ $t("feedback.coordinates") }}</h2> | ||
coordinates can be previewed here | ||
</template> | ||
<template v-slot:success="{ successUrl }"> | ||
<p>{{ $t("feedback.success.thank_you") }}</p> | ||
<p> | ||
{{ $t("feedback.success.response_at") }} | ||
<a id="feedback-success-url" class="btn-link" :href="successUrl">{{ $t("feedback.success.this_pull_request") }}</a> | ||
</p> | ||
</template> | ||
</BasicFeedbackModal> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
@import "@/assets/variables"; | ||
.modal { | ||
#additional_context { | ||
min-width: 100%; | ||
} | ||
} | ||
</style> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
<script setup lang="ts"> | ||
import { useGlobalStore } from "@/stores/global"; | ||
import { ref } from "vue"; | ||
import { useI18n } from "vue-i18n"; | ||
import TokenBasedModal from "@/components/feedback/TokenBasedModal.vue"; | ||
const { t } = useI18n({ inheritLocale: true, useScope: "global" }); | ||
const global = useGlobalStore(); | ||
const deleteIssueRequested = ref(false); | ||
</script> | ||
|
||
<template> | ||
<TokenBasedModal :data="global.feedback.data"> | ||
<template v-slot:modal> | ||
<div class="form-group"> | ||
<div id="feedback-coordinate-picker-helptext" class="d-none toast toast-primary"> | ||
{{ $t("feedback.coordinatepicker.helptext.enter_serveral") }}<br /> | ||
{{ $t("feedback.coordinatepicker.helptext.saved_for_12h") }}<br /> | ||
</div> | ||
<label class="form-label" for="feedback-subject"> {{ $t("feedback.subject") }}</label> | ||
<div class="input-group"> | ||
<select | ||
class="form-select" | ||
id="feedback-category" | ||
:aria-label="$t('feedback.category')" | ||
v-model="global.feedback.data.category" | ||
> | ||
<option value="general">{{ $t("feedback.type.general") }}</option> | ||
<option value="bug">{{ $t("feedback.type.bug") }}</option> | ||
<option value="features">{{ $t("feedback.type.features") }}</option> | ||
<option value="search">{{ $t("feedback.type.search") }}</option> | ||
<option value="entry">{{ $t("feedback.type.entry") }}</option> | ||
</select> | ||
<input | ||
class="form-input" | ||
type="text" | ||
:placeholder="$t('feedback.subject')" | ||
v-model="global.feedback.data.subject" | ||
id="feedback-subject" | ||
/> | ||
</div> | ||
</div> | ||
|
||
<div class="form-group"> | ||
<div> | ||
<label class="form-label" for="feedback-body"> | ||
{{ $t("feedback.message") }} | ||
</label> | ||
<button | ||
id="feedback-coordinate-picker" | ||
v-if="global.feedback.data.category === 'entry'" | ||
class="btn btn-sm btn-link" | ||
> | ||
{{ $t("feedback.coordinatepicker.title") }} | ||
</button> | ||
</div> | ||
<textarea | ||
class="form-input" | ||
id="feedback-body" | ||
:placeholder="$t('feedback.message')" | ||
v-model="global.feedback.data.body" | ||
rows="6" | ||
> | ||
</textarea> | ||
<p class="text-gray text-tiny"> | ||
{{ | ||
{ | ||
general: t("feedback.helptext.general"), | ||
bug: t("feedback.helptext.bug"), | ||
feature: t("feedback.helptext.features"), | ||
search: t("feedback.helptext.search"), | ||
entry: t("feedback.helptext.entry"), | ||
other: t("feedback.helptext.other"), // This is only here to make the linter happy, backend uses "other" as a fallback if the category is not known | ||
}[global.feedback.data.category] | ||
}} | ||
</p> | ||
</div> | ||
|
||
<!-- only visible if called through a view, because then the context of the calling building is availible --> | ||
<div> | ||
<button id="feedback-coordinate-picker" class="btn btn-sm d-none"> | ||
{{ $t("feedback.coordinatepicker.title") }} | ||
</button> | ||
</div> | ||
<div class="form-group"> | ||
<label class="form-checkbox" id="feedback-delete-label"> | ||
<input type="checkbox" id="feedback-delete" v-model="deleteIssueRequested" /> | ||
<i class="form-icon" /> {{ $t("feedback.delete") }} | ||
</label> | ||
</div> | ||
</template> | ||
<template v-slot:success="{ successUrl }"> | ||
<p>{{ $t("feedback.success.thank_you") }}</p> | ||
<p> | ||
{{ $t("feedback.success.response_at") }} | ||
<a id="feedback-success-url" class="btn-link" :href="successUrl">{{ $t("feedback.success.this_issue") }}</a> | ||
</p> | ||
</template> | ||
</TokenBasedModal> | ||
</template> | ||
|
||
<style lang="scss" scoped> | ||
@import "@/assets/variables"; | ||
.modal { | ||
label { | ||
width: fit-content; | ||
display: inline-block; | ||
} | ||
.form-select { | ||
flex: none; | ||
} | ||
#feedback-body { | ||
min-width: 100%; | ||
} | ||
#feedback-coordinate-picker { | ||
float: right; | ||
margin-top: 0.5em; | ||
} | ||
#feedback-coordinate-picker-helptext { | ||
font-size: 14px; | ||
} | ||
} | ||
</style> |
Oops, something went wrong.