diff --git a/web/src/lib/components/base/combobox.svelte b/web/src/lib/components/base/combobox.svelte index 5ef1a812..7c791908 100644 --- a/web/src/lib/components/base/combobox.svelte +++ b/web/src/lib/components/base/combobox.svelte @@ -63,6 +63,9 @@ for (let i = 0; i < dropdownMenu.children.length; i++) { const li = dropdownMenu.children[i]; const textNode = li.getElementsByTagName("p")[0]; + if(!textNode) { + return + } textNode.innerHTML = items[i].text; const text = textNode.innerText.replace( diff --git a/web/src/lib/components/base/toast.svelte b/web/src/lib/components/base/toast.svelte index ce6301e1..216e525c 100644 --- a/web/src/lib/components/base/toast.svelte +++ b/web/src/lib/components/base/toast.svelte @@ -14,6 +14,7 @@ class="fa fa-{t.icon} p-3 mr-2 rounded-lg" class:success-toast={t.type == "success"} class:error-toast={t.type == "error"} + class:warning-toast={t.type == "warning"} >
{t.text}
@@ -38,4 +39,8 @@
color: #ef4444;
background-color: #fecaca;
}
+ .warning-toast {
+ color: #ffb900;
+ background-color: #fef3c6;
+ }
diff --git a/web/src/lib/components/waypoint/waypoint_modal.svelte b/web/src/lib/components/waypoint/waypoint_modal.svelte
index 4d7b0497..a34a94ed 100644
--- a/web/src/lib/components/waypoint/waypoint_modal.svelte
+++ b/web/src/lib/components/waypoint/waypoint_modal.svelte
@@ -88,7 +88,7 @@
setFields("lon", convertDMSToDD(lon, lonDir));
} else {
show_toast({
- text: "No GPS data in image",
+ text: $_('no-gps-data-in-image'),
icon: "close",
type: "error",
});
diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json
index 5f2149c2..768510be 100644
--- a/web/src/lib/i18n/locales/de.json
+++ b/web/src/lib/i18n/locales/de.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Passwort vergessen?",
"french": "Französisch",
"from-file": "Aus einer Datei",
+ "from-photos": "",
"from-url": "Aus einer URL",
"german": "Deutsch",
"get-position-from-exif": "Koordinaten aus EXIF Daten",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Bisher keine Kommentare",
"no-data": "Keine Daten",
"no-description-for-now": "Noch keine Beschreibung",
+ "no-gps-data-in-image": "No GPS data in image",
"no-grid": "Kein Gitter",
"no-notifications": "Keine Benachrichtigungen",
"no-photos-here": "Hier sind noch keine Fotos",
diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json
index cbe1fdd2..e3610173 100644
--- a/web/src/lib/i18n/locales/en.json
+++ b/web/src/lib/i18n/locales/en.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "French",
"from-file": "From file",
+ "from-photos": "From Photos",
"from-url": "From URL",
"german": "German",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
+ "no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos or videos here",
diff --git a/web/src/lib/i18n/locales/es.json b/web/src/lib/i18n/locales/es.json
index 0feae6e2..4f4c476f 100644
--- a/web/src/lib/i18n/locales/es.json
+++ b/web/src/lib/i18n/locales/es.json
@@ -139,6 +139,7 @@
"forgot-your-password": "¿Contraseña olvidada?",
"french": "Francés",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "Alemán",
"get-position-from-exif": "Obtener las coordenadas de los datos EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Ningún comentario todavía",
"no-data": "No datos",
"no-description-for-now": "Ninguna descripción de momento",
+ "no-gps-data-in-image": "",
"no-grid": "Ninguna cuadrícula",
"no-notifications": "No notificaciones",
"no-photos-here": "No fotos aquí",
diff --git a/web/src/lib/i18n/locales/fr.json b/web/src/lib/i18n/locales/fr.json
index 3d0552b8..503f00ad 100644
--- a/web/src/lib/i18n/locales/fr.json
+++ b/web/src/lib/i18n/locales/fr.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Mot de passe oublié ?",
"french": "Français",
"from-file": "Depuis un fichier",
+ "from-photos": "",
"from-url": "Depuis une URL",
"german": "Allemand",
"get-position-from-exif": "Obtenir les coordonnées à partir des données EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Aucun commentaire pour l'instant",
"no-data": "Pas de données",
"no-description-for-now": "Pas de description pour le moment",
+ "no-gps-data-in-image": "",
"no-grid": "Aucune grille",
"no-notifications": "Pas de notifications",
"no-photos-here": "Aucune photo ici",
diff --git a/web/src/lib/i18n/locales/hu.json b/web/src/lib/i18n/locales/hu.json
index 3f51e790..f4c7ecdc 100644
--- a/web/src/lib/i18n/locales/hu.json
+++ b/web/src/lib/i18n/locales/hu.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Francia",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "Német",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
+ "no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",
diff --git a/web/src/lib/i18n/locales/it.json b/web/src/lib/i18n/locales/it.json
index 01735b29..81b59429 100644
--- a/web/src/lib/i18n/locales/it.json
+++ b/web/src/lib/i18n/locales/it.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Password dimenticata?",
"french": "Francese",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "Tedesco",
"get-position-from-exif": "Ottieni posizione da dati EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Nessun commento per il momento",
"no-data": "Nessun dato",
"no-description-for-now": "Nessuna descrizione per il momento",
+ "no-gps-data-in-image": "",
"no-grid": "Nessuna griglia",
"no-notifications": "Nessuna notifica",
"no-photos-here": "Nessuna foto qui",
diff --git a/web/src/lib/i18n/locales/nl.json b/web/src/lib/i18n/locales/nl.json
index afce5af9..2c2b96cb 100644
--- a/web/src/lib/i18n/locales/nl.json
+++ b/web/src/lib/i18n/locales/nl.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Frans",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "Duits",
"get-position-from-exif": "Get coordinates from EXIF data",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "No data",
"no-description-for-now": "No description for now",
+ "no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",
diff --git a/web/src/lib/i18n/locales/pl.json b/web/src/lib/i18n/locales/pl.json
index b7959abd..1240acd5 100644
--- a/web/src/lib/i18n/locales/pl.json
+++ b/web/src/lib/i18n/locales/pl.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Zapomniałeś hasła?",
"french": "Francuski",
"from-file": "Z pliku",
+ "from-photos": "",
"from-url": "Z URL",
"german": "Niemiecki",
"get-position-from-exif": "Odczytaj współrzędne z danych EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "Nie ma jeszcze komentarzy",
"no-data": "Brak danych",
"no-description-for-now": "Nie ma jeszcze opisu",
+ "no-gps-data-in-image": "",
"no-grid": "Brak Siatki",
"no-notifications": "Brak powiadomień",
"no-photos-here": "Nie ma tu zdjęć",
diff --git a/web/src/lib/i18n/locales/pt.json b/web/src/lib/i18n/locales/pt.json
index dc500f39..975b9bdb 100644
--- a/web/src/lib/i18n/locales/pt.json
+++ b/web/src/lib/i18n/locales/pt.json
@@ -139,6 +139,7 @@
"forgot-your-password": "Forgot your password?",
"french": "Francês",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "Alemão",
"get-position-from-exif": "Obter coordenadas dos dados EXIF",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "Sem dados",
"no-description-for-now": "No description for now",
+ "no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",
diff --git a/web/src/lib/i18n/locales/zh.json b/web/src/lib/i18n/locales/zh.json
index 659c0e0c..f4adf8cb 100644
--- a/web/src/lib/i18n/locales/zh.json
+++ b/web/src/lib/i18n/locales/zh.json
@@ -139,6 +139,7 @@
"forgot-your-password": "忘记密码?",
"french": "法语",
"from-file": "From file",
+ "from-photos": "",
"from-url": "From URL",
"german": "德语",
"get-position-from-exif": "从EXIF数据获取坐标",
@@ -212,6 +213,7 @@
"no-comments-so-far": "No comments so far",
"no-data": "无数据",
"no-description-for-now": "No description for now",
+ "no-gps-data-in-image": "",
"no-grid": "No Grid",
"no-notifications": "No notifications",
"no-photos-here": "No photos here",
diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte
index 180c278a..a7807eef 100644
--- a/web/src/routes/trail/edit/[id]/+page.svelte
+++ b/web/src/routes/trail/edit/[id]/+page.svelte
@@ -45,7 +45,7 @@
setRoute,
} from "$lib/stores/valhalla_store";
import { waypoint } from "$lib/stores/waypoint_store";
- import { getFileURL } from "$lib/util/file_util";
+ import { getFileURL, readAsDataURLAsync } from "$lib/util/file_util";
import {
formatDistance,
formatElevation,
@@ -65,6 +65,7 @@
} from "$lib/components/base/search.svelte";
import RoutingOptionsPopup from "$lib/components/trail/routing_options_popup.svelte";
import { TagCreateSchema } from "$lib/models/api/tag_schema.js";
+ import { convertDMSToDD } from "$lib/models/gpx/utils.js";
import { Tag } from "$lib/models/tag.js";
import {
searchLocationReverse,
@@ -77,6 +78,7 @@
createAnchorMarker,
createEditTrailMapPopup,
} from "$lib/util/maplibre_util";
+ import EXIF from "$lib/vendor/exif-js/exif.js";
import { validator } from "@felte/validator-zod";
import cryptoRandomString from "crypto-random-string";
import { createForm } from "felte";
@@ -899,6 +901,53 @@
const result = await tags_index(q);
tagItems = result.items.map((t) => ({ text: t.name, value: t }));
}
+
+ function openPhotoBrowser() {
+ document.getElementById("waypoint-photo-input")!.click();
+ }
+
+ async function handleWaypointPhotoSelection() {
+ const files = (
+ document.getElementById("waypoint-photo-input") as HTMLInputElement
+ ).files;
+
+ if (!files) {
+ return;
+ }
+
+ for (const file of files) {
+ const coords = await new Promise
{$_("photos")}