new setting: begin drawing a new trail from current location (#592)

* new setting: begin drawing a new trail from  current location

* apply only when drawing

* fix application when editing an existing route

* documentation

* ignore pointer events on location circles

* fix i18n

---------

Co-authored-by: Flomp <Flomp@users.noreply.github.com>
Co-authored-by: Christian Beutel <>
This commit is contained in:
slothful-vassal
2026-02-28 10:15:43 +01:00
committed by GitHub
parent 230f42d639
commit bd293e6739
19 changed files with 151 additions and 19 deletions

View File

@@ -0,0 +1,41 @@
package migrations
import (
"github.com/pocketbase/pocketbase/core"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(app core.App) error {
collection, err := app.FindCollectionByNameOrId("uavt73rsqcn1n13")
if err != nil {
return err
}
// add field
if err := collection.Fields.AddMarshaledJSONAt(12, []byte(`{
"hidden": false,
"id": "json1001103536",
"maxSize": 0,
"name": "behavior",
"presentable": false,
"required": false,
"system": false,
"type": "json"
}`)); err != nil {
return err
}
return app.Save(collection)
}, func(app core.App) error {
collection, err := app.FindCollectionByNameOrId("uavt73rsqcn1n13")
if err != nil {
return err
}
// remove field
collection.Fields.RemoveById("json1001103536")
return app.Save(collection)
})
}

View File

@@ -38,3 +38,12 @@ To add the respective URLs navigate to `Settings -> Display` and add them in the
2. Locate the compass control in the top-right corner of the map. 2. Locate the compass control in the top-right corner of the map.
3. Click and drag the compass control to tilt the map into 3D mode. 3. Click and drag the compass control to tilt the map into 3D mode.
4. Adjust the tilt and rotation as desired to view the terrain in 3D. 4. Adjust the tilt and rotation as desired to view the terrain in 3D.
## Route drawing behavior
You can configure how new route drawing starts in `Settings -> Display`.
- Enable `Begin drawing a new trail from your current location` to automatically center route drawing on your current GPS location.
- Disable it to start drawing at the current map view instead.
This option only affects creating a **new** trail in the route editor.

View File

@@ -5,7 +5,6 @@
import type { Trail } from "$lib/models/trail"; import type { Trail } from "$lib/models/trail";
import type { Waypoint } from "$lib/models/waypoint"; import type { Waypoint } from "$lib/models/waypoint";
import { theme } from "$lib/stores/theme_store"; import { theme } from "$lib/stores/theme_store";
import { fetchGPX } from "$lib/stores/trail_store";
import { findStartAndEndPoints } from "$lib/util/geojson_util"; import { findStartAndEndPoints } from "$lib/util/geojson_util";
import { import {
createMarkerFromWaypoint, createMarkerFromWaypoint,
@@ -69,6 +68,7 @@
trail: Trail, trail: Trail,
) => void; ) => void;
oninit?: (map: M.Map) => void; oninit?: (map: M.Map) => void;
autoGeolocateOnDrawing?: boolean;
} }
let { let {
@@ -99,6 +99,7 @@
onclick, onclick,
onUnclusteredClick, onUnclusteredClick,
oninit, oninit,
autoGeolocateOnDrawing = false,
}: Props = $props(); }: Props = $props();
let mapContainer: HTMLDivElement; let mapContainer: HTMLDivElement;
@@ -592,6 +593,10 @@
if (trails[activeTrail]) { if (trails[activeTrail]) {
removeStartEndMarkers(trails[activeTrail].id); removeStartEndMarkers(trails[activeTrail].id);
} }
if (autoGeolocateOnDrawing) {
geolocate();
}
} }
function stopDrawing() { function stopDrawing() {
@@ -731,6 +736,8 @@
} }
} }
let geolocateControl : M.GeolocateControl;
onMount(async () => { onMount(async () => {
const initialState = { const initialState = {
lng: 0, lng: 0,
@@ -800,8 +807,7 @@
"top-left", "top-left",
); );
map.addControl( geolocateControl = new M.GeolocateControl({
new M.GeolocateControl({
positionOptions: { positionOptions: {
enableHighAccuracy: true, enableHighAccuracy: true,
}, },
@@ -809,7 +815,8 @@
animate: fitBounds == "animate", animate: fitBounds == "animate",
}, },
trackUserLocation: true, trackUserLocation: true,
})); });
map.addControl(geolocateControl);
if (showStyleSwitcher) { if (showStyleSwitcher) {
map.addControl(switcherControl); map.addControl(switcherControl);
@@ -901,6 +908,17 @@
showWaypoints(); showWaypoints();
}); });
function geolocate() {
if (!page.data.settings?.behavior) return;
if (page.data.settings.behavior.allowAutoGeolocate === true) {
if (geolocateControl._watchState === 'OFF') {
geolocateControl.options.trackUserLocation = true;
geolocateControl.trigger();
}
}
}
onDestroy(() => { onDestroy(() => {
map?.remove(); map?.remove();
}); });
@@ -982,4 +1000,8 @@
padding-bottom: 2.5px; padding-bottom: 2.5px;
@apply bg-menu-item-background-focus w-3 aspect-square rounded-full; @apply bg-menu-item-background-focus w-3 aspect-square rounded-full;
} }
:global(.maplibregl-user-location-accuracy-circle, .maplibregl-user-location-dot) {
pointer-events: none;
}
</style> </style>

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Routen hinzugefügt zu", "added-trails-to": "Routen hinzugefügt zu",
"after": "Nach", "after": "Nach",
"all-activities": "Alle Aktivitäten", "all-activities": "Alle Aktivitäten",
"allow-auto-geolocate": "Beginne das Zeichnen einer neuen Route am aktuellen Standort",
"alphabetical": "Alphabetisch", "alphabetical": "Alphabetisch",
"already-account": "Du hast bereits ein Konto?", "already-account": "Du hast bereits ein Konto?",
"altitude": "Höhe", "altitude": "Höhe",
@@ -35,6 +36,7 @@
"basic-info": "Basisinformation", "basic-info": "Basisinformation",
"basque": "Baskisch", "basque": "Baskisch",
"before": "Vor", "before": "Vor",
"behavior": "Verhalten",
"bicycle-parking": "Fahrrad-Parkplatz", "bicycle-parking": "Fahrrad-Parkplatz",
"bicycle-rental": "Fahrradverleih", "bicycle-rental": "Fahrradverleih",
"bicycle-shop": "Fahrrad-Reparatur", "bicycle-shop": "Fahrrad-Reparatur",
@@ -215,8 +217,8 @@
"invalid-username": "Ungültiger Nutzername", "invalid-username": "Ungültiger Nutzername",
"italian": "Italienisch", "italian": "Italienisch",
"joined": "Beigetreten", "joined": "Beigetreten",
"keep-private": "Ohne Veröffentlichung fortfahren",
"keep-original": "", "keep-original": "",
"keep-private": "Ohne Veröffentlichung fortfahren",
"language": "Sprache", "language": "Sprache",
"latitude": "Breitengrad", "latitude": "Breitengrad",
"layer": "{n, plural, =1 {Ebene} other {Ebenen}}", "layer": "{n, plural, =1 {Ebene} other {Ebenen}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Added trails to", "added-trails-to": "Added trails to",
"after": "After", "after": "After",
"all-activities": "All activities", "all-activities": "All activities",
"allow-auto-geolocate": "Begin drawing a new trail from your current location",
"alphabetical": "Alphabetical", "alphabetical": "Alphabetical",
"already-account": "Already have an account?", "already-account": "Already have an account?",
"altitude": "Altitude", "altitude": "Altitude",
@@ -35,6 +36,7 @@
"basic-info": "Basic Info", "basic-info": "Basic Info",
"basque": "Basque", "basque": "Basque",
"before": "Before", "before": "Before",
"behavior": "Behavior",
"bicycle-parking": "Bicycle Parking", "bicycle-parking": "Bicycle Parking",
"bicycle-rental": "Bicycle Rental", "bicycle-rental": "Bicycle Rental",
"bicycle-shop": "Bicycle Shop", "bicycle-shop": "Bicycle Shop",
@@ -215,8 +217,8 @@
"invalid-username": "Invalid username", "invalid-username": "Invalid username",
"italian": "Italian", "italian": "Italian",
"joined": "Joined", "joined": "Joined",
"keep-private": "Keep private",
"keep-original": "Keep original", "keep-original": "Keep original",
"keep-private": "Keep private",
"language": "Language", "language": "Language",
"latitude": "Latitude", "latitude": "Latitude",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Rutas añadidas a", "added-trails-to": "Rutas añadidas a",
"after": "Después", "after": "Después",
"all-activities": "Todas las actividades", "all-activities": "Todas las actividades",
"allow-auto-geolocate": "",
"alphabetical": "Alfabético", "alphabetical": "Alfabético",
"already-account": "¿Ya tienes una cuenta?", "already-account": "¿Ya tienes una cuenta?",
"altitude": "Altitud", "altitude": "Altitud",
@@ -213,9 +214,9 @@
"invalid-date": "Fecha no válida", "invalid-date": "Fecha no válida",
"invalid-username": "Usuario no válido", "invalid-username": "Usuario no válido",
"italian": "Italiano", "italian": "Italiano",
"keep-private": "Keep private",
"joined": "Afiliado", "joined": "Afiliado",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Idioma", "language": "Idioma",
"latitude": "Latitud", "latitude": "Latitud",
"layer": "{n, plural, one {}=1 {Lista} other {Listas}}", "layer": "{n, plural, one {}=1 {Lista} other {Listas}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Ibilbideak hona gehitu dira", "added-trails-to": "Ibilbideak hona gehitu dira",
"after": "Ondoren", "after": "Ondoren",
"all-activities": "Ekintza guztiak", "all-activities": "Ekintza guztiak",
"allow-auto-geolocate": "",
"alphabetical": "Alfabetikoa", "alphabetical": "Alfabetikoa",
"already-account": "Baduzu kontua lehendik?", "already-account": "Baduzu kontua lehendik?",
"altitude": "Altuera", "altitude": "Altuera",
@@ -215,8 +216,8 @@
"invalid-username": "Erabiltzailea ez da zuzena", "invalid-username": "Erabiltzailea ez da zuzena",
"italian": "Italiera", "italian": "Italiera",
"joined": "Sartu da", "joined": "Sartu da",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Hizkuntza", "language": "Hizkuntza",
"latitude": "Latitudea", "latitude": "Latitudea",
"layer": "{n, plural, one {}=1 {geruza} other {geruza}}", "layer": "{n, plural, one {}=1 {geruza} other {geruza}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Ajouter les itinéraires à", "added-trails-to": "Ajouter les itinéraires à",
"after": "Après", "after": "Après",
"all-activities": "Toutes les activités", "all-activities": "Toutes les activités",
"allow-auto-geolocate": "",
"alphabetical": "Alphabétique", "alphabetical": "Alphabétique",
"already-account": "Déjà un compte ?", "already-account": "Déjà un compte ?",
"altitude": "Altitude", "altitude": "Altitude",
@@ -215,8 +216,8 @@
"invalid-username": "Nom d'utilisateur invalide", "invalid-username": "Nom d'utilisateur invalide",
"italian": "Italien", "italian": "Italien",
"joined": "Rejoint", "joined": "Rejoint",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Langue", "language": "Langue",
"latitude": "Latitude", "latitude": "Latitude",
"layer": "{n, plural, =1 {Calque} other {Calques}}", "layer": "{n, plural, =1 {Calque} other {Calques}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Hozzáadott nyomvonalak a", "added-trails-to": "Hozzáadott nyomvonalak a",
"after": "After", "after": "After",
"all-activities": "All activities", "all-activities": "All activities",
"allow-auto-geolocate": "",
"alphabetical": "Betűrendben", "alphabetical": "Betűrendben",
"already-account": "Már rendelkezik fiókkal?", "already-account": "Már rendelkezik fiókkal?",
"altitude": "Magasság", "altitude": "Magasság",
@@ -215,8 +216,8 @@
"invalid-username": "Érvénytelen felhasználó", "invalid-username": "Érvénytelen felhasználó",
"italian": "Olasz", "italian": "Olasz",
"joined": "Joined", "joined": "Joined",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Nyelf", "language": "Nyelf",
"latitude": "Szélesség", "latitude": "Szélesség",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Percorsi aggiunto a", "added-trails-to": "Percorsi aggiunto a",
"after": "Dopo", "after": "Dopo",
"all-activities": "Tutte le Attività", "all-activities": "Tutte le Attività",
"allow-auto-geolocate": "",
"alphabetical": "Alfabetico", "alphabetical": "Alfabetico",
"already-account": "Hai già un account?", "already-account": "Hai già un account?",
"altitude": "Altitudine", "altitude": "Altitudine",
@@ -215,8 +216,8 @@
"invalid-username": "Nome utente non valido", "invalid-username": "Nome utente non valido",
"italian": "Italiano", "italian": "Italiano",
"joined": "Aggiunto", "joined": "Aggiunto",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Lingua", "language": "Lingua",
"latitude": "Latitudine", "latitude": "Latitudine",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Route toegevoegd aan", "added-trails-to": "Route toegevoegd aan",
"after": "Na", "after": "Na",
"all-activities": "Alle activiteiten", "all-activities": "Alle activiteiten",
"allow-auto-geolocate": "",
"alphabetical": "Alfabetisch", "alphabetical": "Alfabetisch",
"already-account": "Heb je al een account?", "already-account": "Heb je al een account?",
"altitude": "Hoogte", "altitude": "Hoogte",
@@ -216,8 +217,8 @@
"invalid-username": "Ongeldige gebruikersnaam", "invalid-username": "Ongeldige gebruikersnaam",
"italian": "Italiaans", "italian": "Italiaans",
"joined": "Aangesloten", "joined": "Aangesloten",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Taal", "language": "Taal",
"latitude": "Breedtegraad", "latitude": "Breedtegraad",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Dodaj szlaki do", "added-trails-to": "Dodaj szlaki do",
"after": "Po", "after": "Po",
"all-activities": "Wszystkie aktywności", "all-activities": "Wszystkie aktywności",
"allow-auto-geolocate": "",
"alphabetical": "Alfabetyczne", "alphabetical": "Alfabetyczne",
"already-account": "Czy masz już konto?", "already-account": "Czy masz już konto?",
"altitude": "Wysokość", "altitude": "Wysokość",
@@ -215,8 +216,8 @@
"invalid-username": "Błędna nazwa użytkownika", "invalid-username": "Błędna nazwa użytkownika",
"italian": "Włoski", "italian": "Włoski",
"joined": "Dołączono", "joined": "Dołączono",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Język", "language": "Język",
"latitude": "Szerokość", "latitude": "Szerokość",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "trilhas adicionada para", "added-trails-to": "trilhas adicionada para",
"after": "Depois", "after": "Depois",
"all-activities": "Todas as atividades", "all-activities": "Todas as atividades",
"allow-auto-geolocate": "",
"alphabetical": "Alfabético", "alphabetical": "Alfabético",
"already-account": "Já tem uma conta?", "already-account": "Já tem uma conta?",
"altitude": "Altitude", "altitude": "Altitude",
@@ -215,8 +216,8 @@
"invalid-username": "Nome de usuário inválido", "invalid-username": "Nome de usuário inválido",
"italian": "Italiano", "italian": "Italiano",
"joined": "Joined", "joined": "Joined",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Língua", "language": "Língua",
"latitude": "Latitude", "latitude": "Latitude",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "Added trails to", "added-trails-to": "Added trails to",
"after": "После", "after": "После",
"all-activities": "Все активности", "all-activities": "Все активности",
"allow-auto-geolocate": "",
"alphabetical": "По алфавиту", "alphabetical": "По алфавиту",
"already-account": "Уже есть аккаунт?", "already-account": "Уже есть аккаунт?",
"altitude": "Высота", "altitude": "Высота",
@@ -215,8 +216,8 @@
"invalid-username": "Некорректное имя пользователя", "invalid-username": "Некорректное имя пользователя",
"italian": "Итальянский", "italian": "Итальянский",
"joined": "Зарегистрирован", "joined": "Зарегистрирован",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "Язык", "language": "Язык",
"latitude": "Широта", "latitude": "Широта",
"layer": "{n, plural, =1 {Layer} other {Layers}}", "layer": "{n, plural, =1 {Layer} other {Layers}}",

View File

@@ -17,6 +17,7 @@
"added-trails-to": "添加路线到", "added-trails-to": "添加路线到",
"after": "之后", "after": "之后",
"all-activities": "所有活动", "all-activities": "所有活动",
"allow-auto-geolocate": "",
"alphabetical": "字母", "alphabetical": "字母",
"already-account": "已注册账户?", "already-account": "已注册账户?",
"altitude": "海拔", "altitude": "海拔",
@@ -215,8 +216,8 @@
"invalid-username": "无效用户名", "invalid-username": "无效用户名",
"italian": "意大利语", "italian": "意大利语",
"joined": "已加入", "joined": "已加入",
"keep-private": "Keep private",
"keep-original": "", "keep-original": "",
"keep-private": "Keep private",
"language": "语言", "language": "语言",
"latitude": "纬度", "latitude": "纬度",
"layer": "{n, plural, =1 {层} other {层}}", "layer": "{n, plural, =1 {层} other {层}}",

View File

@@ -21,8 +21,8 @@ const SettingsCreateSchema = z.object({
trails: z.enum(["public", "private"]), trails: z.enum(["public", "private"]),
lists: z.enum(["public", "private"]) lists: z.enum(["public", "private"])
}).optional().nullable(), }).optional().nullable(),
notifications: z.record(z.enum(Object.values(NotificationType) as [string, ...string[]]), z.object({ web: z.boolean(), email: z.boolean() })).optional().nullable() notifications: z.record(z.enum(Object.values(NotificationType) as [string, ...string[]]), z.object({ web: z.boolean(), email: z.boolean() })).optional().nullable(),
behavior: z.object({ allowAutoGeolocate: z.boolean() }).optional().nullable(),
}) satisfies ZodType<Settings> }) satisfies ZodType<Settings>
ZodType<Partial<Comment>> ZodType<Partial<Comment>>

View File

@@ -30,6 +30,7 @@ class Settings {
user?: string; user?: string;
privacy?: { account: "public" | "private", trails: "public" | "private", lists: "public" | "private" } | null privacy?: { account: "public" | "private", trails: "public" | "private", lists: "public" | "private" } | null
notifications?: Record<NotificationType, { web: boolean, email: boolean }> | null notifications?: Record<NotificationType, { web: boolean, email: boolean }> | null
behavior?: Behavior | null;
constructor( constructor(
unit: "metric" | "imperial", unit: "metric" | "imperial",
@@ -54,5 +55,9 @@ class Settings {
} }
} }
export type Behavior = {
allowAutoGeolocate: boolean;
}
export { Settings }; export { Settings };

View File

@@ -14,13 +14,41 @@
} from "$lib/stores/search_store"; } from "$lib/stores/search_store";
import { settings_update } from "$lib/stores/settings_store"; import { settings_update } from "$lib/stores/settings_store";
import { currentUser } from "$lib/stores/user_store"; import { currentUser } from "$lib/stores/user_store";
import { country_codes } from "$lib/util/country_code_util";
import { getIconForLocation } from "$lib/util/icon_util"; import { getIconForLocation } from "$lib/util/icon_util";
import { onMount } from "svelte"; import { onMount } from "svelte";
import { _ } from "svelte-i18n"; import { _ } from "svelte-i18n";
import Toggle from "$lib/components/base/toggle.svelte";
import { show_toast } from "$lib/stores/toast_store.svelte.js";
let settings = $derived(page.data.settings); let settings = $derived(page.data.settings);
let allowAutoGeolocate = $state(
page.data.settings.behavior?.allowAutoGeolocate ?? false,
);
async function handleAllowAutoGeolocateChange() {
if (!settings) {
return;
}
try {
if (!settings.behavior) {
settings.behavior = { allowAutoGeolocate: allowAutoGeolocate };
} else {
settings.behavior.allowAutoGeolocate = allowAutoGeolocate;
}
await settings_update(settings);
} catch (e) {
show_toast({
type: "error",
icon: "close",
text: "Error updating behavior settings",
});
console.error(e);
}
}
const mapFocus: SelectItem[] = [ const mapFocus: SelectItem[] = [
{ text: $_("trail", { values: { n: 2 } }), value: "trails" }, { text: $_("trail", { values: { n: 2 } }), value: "trails" },
{ text: $_("location"), value: "location" }, { text: $_("location"), value: "location" },
@@ -138,6 +166,18 @@
></Search> ></Search>
</div> </div>
{/if} {/if}
<div
class="mt-4 grid gap-4"
style="grid-template-columns: 1fr min-content ;"
>
<p>{$_("allow-auto-geolocate")}</p>
<div>
<Toggle
bind:value={allowAutoGeolocate}
onchange={handleAllowAutoGeolocateChange}
></Toggle>
</div>
</div>
</div> </div>
<div> <div>
<h4 class="text-xl font-medium mb-2">{$_("tilesets")}</h4> <h4 class="text-xl font-medium mb-2">{$_("tilesets")}</h4>

View File

@@ -1504,6 +1504,7 @@
waypoints={$formData.expand?.waypoints_via_trail} waypoints={$formData.expand?.waypoints_via_trail}
drawing={drawingActive} drawing={drawingActive}
showTerrain={true} showTerrain={true}
autoGeolocateOnDrawing={page.params.id === "new"}
onmarkerdragend={moveMarker} onmarkerdragend={moveMarker}
activeTrail={0} activeTrail={0}
bind:map bind:map