+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());
+ }
+}
+
+
+
+
+
+
+
+
+
diff --git a/webclient/src/locales/de.yaml b/webclient/src/locales/de.yaml
index a2d3704f1..0134f9ecf 100644
--- a/webclient/src/locales/de.yaml
+++ b/webclient/src/locales/de.yaml
@@ -11,6 +11,11 @@ core_js:
feedback:
cancel: Abbrechen
category: Feedback-Kategorie
+ coordinatepicker:
+ helptext:
+ enter_serveral: Falls du mehrere Koordinaten gleichzeitig korrigieren willst, kannst du dieses Fenster schließen und weitere Koordinaten editieren.
+ saved_for_12h: Wir speichern deine Veränderungen für 12 Stunden bei dir lokal.
+ title: Koordinate auswählen
delete: Das zugehörige GitHub Issue löschen, sobald es gelöst wurde.
error:
feedback_not_configured: Das Senden von Feedback ist auf dem Server aktuell nicht konfiguriert.
@@ -55,6 +60,7 @@ feedback:
response_at: Antwort auf dein Feedback findest du auf
thank_you: Vielen Dank für dein Feedback! Wir werden es schnellstmöglich bearbeiten.
this_issue: diesem GitHub Issue
+ this_pull_request: diesem GitHub Pull Request
title: Vielen Dank!
title: Feedback senden
type:
@@ -173,7 +179,20 @@ view_view:
meta:
details_for: Details für
msg:
+ coordinate-counter:
+ delete: Löschen
+ delete-confirm: Wirklich löschen?
+ info: Änderungen werden lokal \n für 12h gespeichert
+ msg-1: Aktuell
+ msg-2: ausstehende Koordinatenänderung|ausstehende Koordinatenänderungen
+ send: Senden
+ correct_location:
+ btn-cancel: Abbrechen
+ btn-done: Fertig
+ msg: Um die richtige Position zu setzen, ziehe den roten Marker über die Karte.
inaccurate_only_building:
+ btn: Koordinate eintragen
+ help_others_and: Falls du sie herausfindest, kannst du die
primary_msg: Die angezeigte Position zeigt nur die Position des Gebäude(teils). Die genaue Lage innerhalb des Gebäudes ist uns nicht bekannt.
no_floor_overlay: Für den angezeigten Raum gibt es leider keine Indoor Karte.
roomfinder_modal:
diff --git a/webclient/src/locales/en.yaml b/webclient/src/locales/en.yaml
index 173e6c6b1..5798bd028 100644
--- a/webclient/src/locales/en.yaml
+++ b/webclient/src/locales/en.yaml
@@ -11,6 +11,11 @@ core_js:
feedback:
cancel: Cancel
category: Feedback category
+ coordinatepicker:
+ helptext:
+ enter_serveral: If you want to correct several coordinates at the same time, you can close this window and edit more coordinates.
+ saved_for_12h: We will save your changes locally for 12 hours.
+ title: Select coordinate
delete: Delete this GitHub issue when resolved.
error:
feedback_not_configured: Sending feedback is currently not configured on the server.
@@ -55,6 +60,7 @@ feedback:
response_at: You can see our response at
thank_you: Thank you for giving your feedback. We will work on this as soon as possible.
this_issue: this GitHub issue
+ this_pull_request: this GitHub pull request
title: Thank you!
title: Send Feedback
type:
diff --git a/webclient/src/pages/view/[id].vue b/webclient/src/pages/view/[id].vue
index e6e9811ad..7a1be0751 100644
--- a/webclient/src/pages/view/[id].vue
+++ b/webclient/src/pages/view/[id].vue
@@ -5,7 +5,7 @@ import DetailsRoomOverviewSection from "@/components/DetailsRoomOverviewSection.
import DetailsBuildingOverviewSection from "@/components/DetailsBuildingOverviewSection.vue";
import DetailsInfoSection from "@/components/DetailsInfoSection.vue";
import DetailsSources from "@/components/DetailsSources.vue";
-import DetailsFeedbackButton from "@/components/DetailsFeedbackButton.vue";
+import DetailsFeedbackButton from "@/components/feedback/DetailsFeedbackButton.vue";
import DetailsRoomfinderMap from "@/components/DetailsRoomfinderMap.vue";
//import DetailsFeaturedOverviewSection from "@/components/DetailsFeaturedOverviewSection.vue";
import { useI18n } from "vue-i18n";
@@ -51,6 +51,11 @@ watchEffect(() => {
const state = useDetailsStore();
const clipboardSource = computed(() => `https://nav.tum.de${route.fullPath}`);
const { copy, copied, isSupported: clipboardIsSupported } = useClipboard({ source: clipboardSource });
+// Coordinate picker states
+const coord_counter = ref({
+ counter: null as number | null,
+ to_confirm_delete: false,
+});
function genDescription(d: DetailsResponse) {
const detailsFor = t("view_view.meta.details_for");
@@ -64,6 +69,18 @@ function genDescription(d: DetailsResponse) {
return description;
}
// --- Loading components ---
+function deletePendingCoordinates() {
+ if (coord_counter.value.to_confirm_delete) {
+ removeLocalStorage("feedback-coords");
+ coord_counter.value.to_confirm_delete = false;
+ state.coord_picker.body_backup = null;
+ state.coord_picker.subject_backup = null;
+ state.coord_picker.backup_id = null;
+ } else {
+ coord_counter.value.to_confirm_delete = true;
+ }
+}
+
function tryToLoadMap() {
/**
* Try to load the entry map (interactive or roomfinder). It requires the map container
@@ -83,12 +100,19 @@ const feedbackButton = ref | null>(nu
const interactiveMap = ref | null>(null);
const roomfinderMap = ref | null>(null);
onMounted(() => {
+ // Update pending coordinate counter on localStorage changes
+ const updateCoordinateCounter = function () {
+ const coords = getLocalStorageWithExpiry<{ [index: string]: Coord }>("feedback-coords", {});
+ coord_counter.value.counter = Object.keys(coords).length;
+ };
+ window.addEventListener("storage", updateCoordinateCounter);
window.addEventListener("resize", () => {
if (state.map.selected === selectedMap.roomfinder) {
roomfinderMap.value?.loadRoomfinderMap(state.map.roomfinder.selected_index);
roomfinderMap.value?.loadRoomfinderModalMap();
}
});
+ updateCoordinateCounter();
nextTick(() => {
// Even though 'mounted' is called there is no guarantee apparently,
@@ -126,6 +150,44 @@ onMounted(() => {
/>
+
+