diff --git a/db/integrations/hammerhead/hammerhead.go b/db/integrations/hammerhead/hammerhead.go index 59f9e98c..34b8a9d3 100644 --- a/db/integrations/hammerhead/hammerhead.go +++ b/db/integrations/hammerhead/hammerhead.go @@ -543,6 +543,7 @@ func createTrailFromActivity(app core.App, detailedTour *HammerheadActivity, gpx "id": trailid, "name": detailedTour.ActivityData.Name, "public": false, + "completed": true, "distance": detailedTour.ActivityData.ActivityInfo[idDistance].Value.Value, "elevation_gain": detailedTour.ActivityData.ActivityInfo[idElevationGain].Value.Value, "elevation_loss": detailedTour.ActivityData.ActivityInfo[idElevationLoss].Value.Value, diff --git a/db/integrations/komoot/komoot.go b/db/integrations/komoot/komoot.go index 65f8bebd..db86f63e 100644 --- a/db/integrations/komoot/komoot.go +++ b/db/integrations/komoot/komoot.go @@ -292,6 +292,7 @@ func createTrailFromTour(app core.App, k *KomootApi, detailedTour *DetailedKomoo "id": trailid, "name": detailedTour.Name, "public": public, + "completed": detailedTour.Type == "tour_recorded", "distance": detailedTour.Distance, "elevation_gain": detailedTour.ElevationUp, "elevation_loss": detailedTour.ElevationDown, diff --git a/db/migrations/1776246553_updated_trails.go b/db/migrations/1776246553_updated_trails.go new file mode 100644 index 00000000..2c5f1304 --- /dev/null +++ b/db/migrations/1776246553_updated_trails.go @@ -0,0 +1,62 @@ +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("e864strfxo14pm4") + if err != nil { + return err + } + + // add field + if err := collection.Fields.AddMarshaledJSONAt(5, []byte(`{ + "hidden": false, + "id": "bool989355118", + "name": "completed", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }`)); err != nil { + return err + } + + err = app.Save(collection) + if err != nil { + return err + } + + logs, err := app.FindAllRecords("summit_logs") + if err != nil { + return err + } + + for _, l := range logs { + trail, err := app.FindRecordById("trails", l.GetString("trail")) + if err != nil { + continue + } + trail.Set("completed", true) + err = app.Save(trail) + if err != nil { + return err + } + } + + return nil + }, func(app core.App) error { + collection, err := app.FindCollectionByNameOrId("e864strfxo14pm4") + if err != nil { + return err + } + + // remove field + collection.Fields.RemoveById("bool989355118") + + return app.Save(collection) + }) +} diff --git a/db/util/meilisearch.go b/db/util/meilisearch.go index 2fee69e8..f57a0750 100644 --- a/db/util/meilisearch.go +++ b/db/util/meilisearch.go @@ -12,7 +12,6 @@ import ( "time" "github.com/meilisearch/meilisearch-go" - "github.com/pocketbase/dbx" "github.com/pocketbase/pocketbase/core" "github.com/tkrajina/gpxgo/gpx" "github.com/twpayne/go-polyline" @@ -52,11 +51,6 @@ func documentFromTrailRecord(app core.App, r *core.Record, author *core.Record, domain = author.GetString("domain") } - logCount, err := app.CountRecords("summit_logs", dbx.NewExp("trail={:id}", dbx.Params{"id": r.Id})) - if err != nil { - return nil, err - } - document := map[string]any{ "id": r.Id, "author": author.Id, @@ -71,7 +65,7 @@ func documentFromTrailRecord(app core.App, r *core.Record, author *core.Record, "duration": r.GetFloat("duration"), "difficulty": difficultyToNumber(r.GetString("difficulty")), "category": category, - "completed": logCount > 0, + "completed": r.GetBool("completed"), "date": r.GetDateTime("date").Time().Unix(), "created": r.GetDateTime("created").Time().Unix(), "public": r.GetBool("public"), diff --git a/web/src/lib/components/trail/trail_card.svelte b/web/src/lib/components/trail/trail_card.svelte index b6052487..698ef6da 100644 --- a/web/src/lib/components/trail/trail_card.svelte +++ b/web/src/lib/components/trail/trail_card.svelte @@ -150,7 +150,9 @@ {/if}
-

{trail.name}

+

+ {trail.name} +

{#if trail.date}

{new Date(trail.date).toLocaleDateString(undefined, { @@ -200,7 +202,11 @@

{#if trail.expand?.category?.name || trail.category}

- {$_(trail.expand?.category?.name ?? trail.category ?? "-")} + {$_( + trail.expand?.category?.name ?? + trail.category ?? + "-", + )}

{/if} {#if trail.location} diff --git a/web/src/lib/components/trail/trail_info_panel.svelte b/web/src/lib/components/trail/trail_info_panel.svelte index 19a076e7..98eb5086 100644 --- a/web/src/lib/components/trail/trail_info_panel.svelte +++ b/web/src/lib/components/trail/trail_info_panel.svelte @@ -56,6 +56,7 @@ import { handleFromRecordWithIRI } from "$lib/util/activitypub_util"; import LikeButton from "./like_button.svelte"; import Editor from "../base/editor.svelte"; + import { trails_update } from "$lib/stores/trail_store"; interface Props { initTrail: Trail; @@ -75,6 +76,7 @@ let summitLogModal: SummitLogModal; let confirmModal: ConfirmModal; + let markTrailAsCompletedModal: ConfirmModal; let trail = $state(untrack(() => initTrail)); @@ -243,6 +245,13 @@ log.trail = trail.id; const newLog = await summit_logs_create(log); summitLogs.set([...$summitLogs, newLog]); + if ( + $summitLogs.length == 1 && + trail.author == $currentUser?.actor && + !trail.completed + ) { + markTrailAsCompletedModal.openModal(); + } } summitLogCreateLoading = false; } @@ -259,6 +268,12 @@ ); summitLogs.set(newSummitLogList); } + + async function markTrailAsCompleted() { + trail.completed = true; + const updatedTrail: Trail = { ...trail }; + await trails_update(trail, updatedTrail); + }
{/if}
1 ? 'grid-cols-[8fr_5fr]' : 'grid-cols-1'} h-80 rounded-t-3xl overflow-hidden cursor-pointer" > @@ -391,10 +406,20 @@ {trail.location} {/if} -

+

{$_(trail.difficulty ?? "?")}

+

+ + {$_( + trail.completed ? "completed" : "not-completed", + )} +

@@ -675,6 +700,16 @@ saveSummitLog(log)} > + + {#each expandedTags ? trail.expand.tags : trail.expand.tags.slice(0, 2) as tag} - + {/each} {#if trail.expand.tags.length > 2} @@ -154,7 +155,9 @@
{#if trail.expand?.category?.name || trail.category}

- {$_(trail.expand?.category?.name ?? trail.category ?? "-")} + {$_( + trail.expand?.category?.name ?? trail.category ?? "-", + )}

{/if} {#if trail.location} @@ -165,6 +168,14 @@

{$_(trail.difficulty ?? "?")}

+

+ + {$_(trail.completed ? "completed" : "not-completed")} +

diff --git a/web/src/lib/i18n/locales/cs.json b/web/src/lib/i18n/locales/cs.json index d41f2f87..0ab73098 100644 --- a/web/src/lib/i18n/locales/cs.json +++ b/web/src/lib/i18n/locales/cs.json @@ -3,6 +3,7 @@ "Canoeing": "Kanoistika", "Climbing": "Horolezectví", "Hiking": "Turistika", + "Skiing": "", "Walking": "Chůze", "about": "O aplikaci", "account-delete-confirm": "Chystáte se odstranit svůj účet. Všechny vaše záznamy budou odstraněny též. Chcete pokračovat?", @@ -264,6 +265,8 @@ "make-thumbnail": "Vytvořit náhled", "map": "Mapa", "map-style": "Styl mapy", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Max. obtížnost túry", "metric": "Metrický", "moderate": "Středně náročné", @@ -291,6 +294,7 @@ "new-password-text": "Nastavit nové heslo", "new-token-generated": "", "new-trail": "Nová trasa", + "no": "", "no-account": "Ještě nemáte účet?", "no-api-tokens": "", "no-comments-so-far": "Zatím žádné komentáře", @@ -467,5 +471,6 @@ "welcome_to": "Vítejte v", "width": "Šířka", "wrong-username-or-password": "Nesprávné uživatelské jméno nebo heslo", + "yes": "", "you-can": "Můžete" } diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json index 7ce03d46..39cc5605 100644 --- a/web/src/lib/i18n/locales/de.json +++ b/web/src/lib/i18n/locales/de.json @@ -3,6 +3,7 @@ "Canoeing": "Kanufahren", "Climbing": "Klettern", "Hiking": "Wandern", + "Skiing": "Skifahren", "Walking": "Laufen", "about": "Über", "account-delete-confirm": "Du bist dabei, dein Konto zu löschen. Alle deine Routen werden ebenfalls gelöscht. Möchtest du fortfahren?", @@ -264,6 +265,8 @@ "make-thumbnail": "Thumbnail festlegen", "map": "Karte", "map-style": "Kartenstil", + "mark-trail-as-completed": "Route als abgeschlossen markieren", + "mark-trail-as-completed-modal-text": "Möchtest du diese Route als abgeschlossen markieren? Du kannst diesen Status jederzeit wieder ändern.", "max-hiking-difficulty": "Max. Schwierigkeit der Wanderung", "metric": "Metrisch", "moderate": "Mittel", @@ -291,6 +294,7 @@ "new-password-text": "Wähle ein neues Passwort", "new-token-generated": "", "new-trail": "Neue Route", + "no": "Nein", "no-account": "Du hast noch kein Konto?", "no-api-tokens": "", "no-comments-so-far": "Bisher keine Kommentare", @@ -467,5 +471,6 @@ "welcome_to": "Willkommen bei", "width": "Breite", "wrong-username-or-password": "Falscher Nutzername oder falsches Passwort", + "yes": "Ja", "you-can": "Du kannst" } diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json index 53143141..d7f0accf 100644 --- a/web/src/lib/i18n/locales/en.json +++ b/web/src/lib/i18n/locales/en.json @@ -3,6 +3,7 @@ "Canoeing": "Canoeing", "Climbing": "Climbing", "Hiking": "Hiking", + "Skiing": "", "Walking": "Walking", "about": "About", "account-delete-confirm": "You are about to delete your account. All your trails will also be deleted. Do you want to proceed?", @@ -21,6 +22,7 @@ "already-account": "Already have an account?", "altitude": "Altitude", "amenity": "Amenity", + "ammenity": "", "api-documentation": "API Documentation", "api-tokens": "API Tokens", "api-tokens-hint": "API Tokens can be used to grant 3rd party applications access to your wanderer account.", @@ -263,6 +265,8 @@ "make-thumbnail": "Make thumbnail", "map": "Map", "map-style": "Map style", + "mark-trail-as-completed": "Mark trail as completed", + "mark-trail-as-completed-modal-text": "Would you like to mark this trail as completed? You can change this status again at any time.", "max-hiking-difficulty": "Max. Hiking Difficulty", "metric": "Metric", "moderate": "Moderate", @@ -290,6 +294,7 @@ "new-password-text": "Set a new password", "new-token-generated": "New API Token generated", "new-trail": "New Trail", + "no": "No", "no-account": "Don't have an account?", "no-api-tokens": "You have no API Tokens", "no-comments-so-far": "No comments so far", @@ -466,5 +471,6 @@ "welcome_to": "Welcome to", "width": "Width", "wrong-username-or-password": "Wrong username or password", + "yes": "Yes", "you-can": "You can" } diff --git a/web/src/lib/i18n/locales/es.json b/web/src/lib/i18n/locales/es.json index 58866c79..343f5147 100644 --- a/web/src/lib/i18n/locales/es.json +++ b/web/src/lib/i18n/locales/es.json @@ -3,6 +3,7 @@ "Canoeing": "Remo", "Climbing": "Escalada", "Hiking": "Senderismo", + "Skiing": "", "Walking": "Paseo", "about": "Sobre", "account-delete-confirm": "Estás al punto de borrar tu cuenta. Todas tus rutas también serán borradas. ¿Quieres proceder?", @@ -264,6 +265,8 @@ "make-thumbnail": "Generar miniaturas", "map": "Mapa", "map-style": "Estilo de mapa", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Dificultad máxima", "metric": "Métrica", "moderate": "Medio", @@ -291,6 +294,7 @@ "new-password-text": "Configura una nueva contraseña", "new-token-generated": "", "new-trail": "Nueva Ruta", + "no": "", "no-account": "¿No tienes una cuenta?", "no-api-tokens": "", "no-comments-so-far": "Ningún comentario todavía", @@ -467,5 +471,6 @@ "welcome_to": "Bienvenid@ a", "width": "Anchura", "wrong-username-or-password": "Usuario o contraseña no correctas", + "yes": "", "you-can": "Puedes" } diff --git a/web/src/lib/i18n/locales/eu.json b/web/src/lib/i18n/locales/eu.json index 8016eb0c..af020b06 100644 --- a/web/src/lib/i18n/locales/eu.json +++ b/web/src/lib/i18n/locales/eu.json @@ -3,6 +3,7 @@ "Canoeing": "Kanoa", "Climbing": "Eskalada", "Hiking": "Mendi-ibilaldia", + "Skiing": "", "Walking": "Oinez", "about": "Honi buruz", "account-delete-confirm": "Zure kontua ezabatzera zoaz. Zure ibilbide guztiak ere ezabatu egingo dira. Jarraitu nahi duzu?", @@ -21,6 +22,7 @@ "already-account": "Baduzu kontua lehendik?", "altitude": "Altuera", "amenity": "Altimetria", + "ammenity": "", "api-documentation": "API dokumentazioa", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Egin iruditxoa", "map": "Mapa", "map-style": "Maparen estiloa", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Mendi ibilaldiaren gehienezko zailtasuna", "metric": "Metrikoa", "moderate": "Erdi-bidekoa", @@ -290,6 +294,7 @@ "new-password-text": "Pasahitz berria sortu", "new-token-generated": "", "new-trail": "Ibilaldi berria", + "no": "", "no-account": "Ez duzu konturik?", "no-api-tokens": "", "no-comments-so-far": "Ez dago iruzkinik", @@ -466,5 +471,6 @@ "welcome_to": "Ongi etorri", "width": "Zabalera", "wrong-username-or-password": "Erabiltzaile izena edo pasahitza okerrak dira", + "yes": "", "you-can": "Hau egin dezakezu" } diff --git a/web/src/lib/i18n/locales/fr.json b/web/src/lib/i18n/locales/fr.json index c01b27f5..1a2ba6b7 100644 --- a/web/src/lib/i18n/locales/fr.json +++ b/web/src/lib/i18n/locales/fr.json @@ -3,6 +3,7 @@ "Canoeing": "Canoë", "Climbing": "Escalade", "Hiking": "Randonnée", + "Skiing": "", "Walking": "Marche", "about": "Informations", "account-delete-confirm": "Vous êtes sur le point de supprimer votre compte. Tous vos itinéraires seront également supprimés. Voulez-vous continuer ?", @@ -264,6 +265,8 @@ "make-thumbnail": "Créer une miniature", "map": "Carte", "map-style": "Style de carte", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Difficulté maximale de marche", "metric": "Métrique", "moderate": "Moyenne", @@ -291,6 +294,7 @@ "new-password-text": "Définir un nouveau mot de passe", "new-token-generated": "", "new-trail": "Nouvel itinéraire", + "no": "", "no-account": "Pas encore de compte ?", "no-api-tokens": "", "no-comments-so-far": "Aucun commentaire pour l'instant", @@ -467,5 +471,6 @@ "welcome_to": "Bienvenue sur", "width": "Largeur", "wrong-username-or-password": "Nom d'utilisateur ou mot de passe incorrect", + "yes": "", "you-can": "Vous pouvez" } diff --git a/web/src/lib/i18n/locales/hu.json b/web/src/lib/i18n/locales/hu.json index 6a2ca179..e08a7aac 100644 --- a/web/src/lib/i18n/locales/hu.json +++ b/web/src/lib/i18n/locales/hu.json @@ -3,6 +3,7 @@ "Canoeing": "Canoeing", "Climbing": "Climbing", "Hiking": "Hiking", + "Skiing": "", "Walking": "Walking", "about": "A programról", "account-delete-confirm": "Ön most a profilját készül törölni. Minden nyomvonala törlődik. Szeretné folytatni?", @@ -21,6 +22,7 @@ "already-account": "Már rendelkezik fiókkal?", "altitude": "Magasság", "amenity": "Amenity", + "ammenity": "", "api-documentation": "API Dokumentáció", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Készítsen miniatűrképet", "map": "Térkép", "map-style": "Map style", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Max. Hiking Difficulty", "metric": "Metrikus", "moderate": "Mérsékelt", @@ -290,6 +294,7 @@ "new-password-text": "Set a new password", "new-token-generated": "", "new-trail": "Új útvonal", + "no": "", "no-account": "Nincs még fiókja?", "no-api-tokens": "", "no-comments-so-far": "No comments so far", @@ -466,5 +471,6 @@ "welcome_to": "Üdvözöljük a", "width": "Width", "wrong-username-or-password": "Helytelen felhasználónév vagy jelszó", + "yes": "", "you-can": "You can" } diff --git a/web/src/lib/i18n/locales/it.json b/web/src/lib/i18n/locales/it.json index 7f7ae48b..7e67dc17 100644 --- a/web/src/lib/i18n/locales/it.json +++ b/web/src/lib/i18n/locales/it.json @@ -3,6 +3,7 @@ "Canoeing": "Canoa", "Climbing": "Arrampicata", "Hiking": "Escursionismo", + "Skiing": "", "Walking": "Camminare", "about": "Su di noi", "account-delete-confirm": "Stai per eliminare il tuo account. Tutti i tuoi percorsi saranno cancellati. Vuoi procedere?", @@ -21,6 +22,7 @@ "already-account": "Hai già un account?", "altitude": "Altitudine", "amenity": "Amenity", + "ammenity": "", "api-documentation": "Documentazione API", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Imposta miniatura", "map": "Mappa", "map-style": "Map style", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Max. Hiking Difficulty", "metric": "Metrico", "moderate": "Moderato", @@ -290,6 +294,7 @@ "new-password-text": "Definire una nuova password", "new-token-generated": "", "new-trail": "Nuovo percorso", + "no": "", "no-account": "Non hai ancora un account?", "no-api-tokens": "", "no-comments-so-far": "Nessun commento per il momento", @@ -466,5 +471,6 @@ "welcome_to": "Benvenuti a", "width": "Width", "wrong-username-or-password": "Nome utente o password errati", + "yes": "", "you-can": "Puoi" } diff --git a/web/src/lib/i18n/locales/nl.json b/web/src/lib/i18n/locales/nl.json index 7726ecb9..85bee31b 100644 --- a/web/src/lib/i18n/locales/nl.json +++ b/web/src/lib/i18n/locales/nl.json @@ -3,6 +3,7 @@ "Canoeing": "Kanoën", "Climbing": "Klimmen", "Hiking": "Hiken", + "Skiing": "", "Walking": "Wandelen", "about": "Over", "account-delete-confirm": "Je staat op het punt je account te verwijderen. Al je routes worden hierdoor eveneens verwijderd. Wil je doorgaan?", @@ -264,6 +265,8 @@ "make-thumbnail": "Miniatuur maken", "map": "Kaart", "map-style": "Kaartstijl", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Max. Hiking Moeilijkheid", "metric": "Metrisch", "moderate": "Gemiddeld", @@ -291,6 +294,7 @@ "new-password-text": "Stel nieuw wachtwoord in", "new-token-generated": "", "new-trail": "Nieuwe Route", + "no": "", "no-account": "Heb je nog geen account?", "no-api-tokens": "", "no-comments-so-far": "Tot nu toe geen opmerkingen", @@ -467,5 +471,6 @@ "welcome_to": "Welkom bij", "width": "Breedte", "wrong-username-or-password": "Onjuiste gebruikersnaam of wachtwoord", + "yes": "", "you-can": "Jij kunt" } diff --git a/web/src/lib/i18n/locales/no.json b/web/src/lib/i18n/locales/no.json index 61049c04..2dda1775 100644 --- a/web/src/lib/i18n/locales/no.json +++ b/web/src/lib/i18n/locales/no.json @@ -265,6 +265,8 @@ "make-thumbnail": "Lag miniatyrbilde", "map": "Kart", "map-style": "Kartstil", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Maks. vanskelighetsgrad", "metric": "Metrisk", "moderate": "Moderat", @@ -292,6 +294,7 @@ "new-password-text": "Sett et nytt passord", "new-token-generated": "Nytt API-token generert", "new-trail": "Ny sti", + "no": "", "no-account": "Har du ikke en konto?", "no-api-tokens": "Du har ingen API-tokens", "no-comments-so-far": "Ingen kommentarer ennå", @@ -468,5 +471,6 @@ "welcome_to": "Velkommen til", "width": "Bredde", "wrong-username-or-password": "Feil brukernavn eller passord", + "yes": "", "you-can": "Du kan" } diff --git a/web/src/lib/i18n/locales/pl.json b/web/src/lib/i18n/locales/pl.json index 4b6821e8..8fa0084b 100644 --- a/web/src/lib/i18n/locales/pl.json +++ b/web/src/lib/i18n/locales/pl.json @@ -3,6 +3,7 @@ "Canoeing": "Kajak", "Climbing": "Wspinaczka", "Hiking": "Wędrówka", + "Skiing": "", "Walking": "Spacer", "about": "Na temat", "account-delete-confirm": "Za chwilę usuniesz swoje konto. Wszystkie twoje szlaki zostaną usunięte. Czy chcesz kontynuować?", @@ -21,6 +22,7 @@ "already-account": "Czy masz już konto?", "altitude": "Wysokość", "amenity": "Amenity", + "ammenity": "", "api-documentation": "Dokumentacja API", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Zrób miniaturkę", "map": "Mapa", "map-style": "Map style", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Maksymalny poziom trudności wędrówki", "metric": "Metryczne", "moderate": "Średni", @@ -290,6 +294,7 @@ "new-password-text": "Ustaw nowe hasło", "new-token-generated": "", "new-trail": "Nowy szlak", + "no": "", "no-account": "Nie masz konta?", "no-api-tokens": "", "no-comments-so-far": "Nie ma jeszcze komentarzy", @@ -466,5 +471,6 @@ "welcome_to": "Witaj w", "width": "Szerokość", "wrong-username-or-password": "Zła nazwa użytkownika lub hasło", + "yes": "", "you-can": "Możesz" } diff --git a/web/src/lib/i18n/locales/pt.json b/web/src/lib/i18n/locales/pt.json index fdbd0fe8..ace21a5c 100644 --- a/web/src/lib/i18n/locales/pt.json +++ b/web/src/lib/i18n/locales/pt.json @@ -3,6 +3,7 @@ "Canoeing": "Canoagem", "Climbing": "Escalada", "Hiking": "Montanhismo", + "Skiing": "", "Walking": "Caminhada", "about": "Sobre", "account-delete-confirm": "Está prestes a excluir a sua conta. Todos os seus percursos também serão excluídos. Quer continuar?", @@ -21,6 +22,7 @@ "already-account": "Já tem uma conta?", "altitude": "Altitude", "amenity": "Amenity", + "ammenity": "", "api-documentation": "Documentação da API", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Faça miniatura", "map": "Mapa", "map-style": "Map style", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Max. Hiking Difficulty", "metric": "Métrica", "moderate": "Moderado", @@ -290,6 +294,7 @@ "new-password-text": "Set a new password", "new-token-generated": "", "new-trail": "Nova trilha", + "no": "", "no-account": "Não tem uma conta?", "no-api-tokens": "", "no-comments-so-far": "No comments so far", @@ -466,5 +471,6 @@ "welcome_to": "Bem-vindo ao", "width": "Width", "wrong-username-or-password": "Nome de utilizador ou palavra-passe errados", + "yes": "", "you-can": "Podes" } diff --git a/web/src/lib/i18n/locales/ru.json b/web/src/lib/i18n/locales/ru.json index 4f5317e8..2c419cc9 100644 --- a/web/src/lib/i18n/locales/ru.json +++ b/web/src/lib/i18n/locales/ru.json @@ -3,6 +3,7 @@ "Canoeing": "Каякинг", "Climbing": "Скалолазание", "Hiking": "Пеший туризм", + "Skiing": "", "Walking": "Прогулка", "about": "О", "account-delete-confirm": "Вы собираетесь удалить аккаунт. Все ваши треки также будут удалены. Продолжить?", @@ -21,6 +22,7 @@ "already-account": "Уже есть аккаунт?", "altitude": "Высота", "amenity": "Amenity", + "ammenity": "", "api-documentation": "Документация API", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "Сделать миниатюру", "map": "Карта", "map-style": "Стиль карты", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "Макс. сложность", "metric": "Метрическая", "moderate": "Средний", @@ -290,6 +294,7 @@ "new-password-text": "Установить новый пароль", "new-token-generated": "", "new-trail": "Новый трек", + "no": "", "no-account": "Нет аккаунта?", "no-api-tokens": "", "no-comments-so-far": "Пока нет комментариев", @@ -466,5 +471,6 @@ "welcome_to": "Добро пожаловать в", "width": "Ширина", "wrong-username-or-password": "Неверный логин или пароль", + "yes": "", "you-can": "Вы можете" } diff --git a/web/src/lib/i18n/locales/zh.json b/web/src/lib/i18n/locales/zh.json index 9c473eea..016fdaca 100644 --- a/web/src/lib/i18n/locales/zh.json +++ b/web/src/lib/i18n/locales/zh.json @@ -3,6 +3,7 @@ "Canoeing": "划艇", "Climbing": "攀岩", "Hiking": "徒步", + "Skiing": "", "Walking": "步行", "about": "关于", "account-delete-confirm": "您现在要删除当前账户,所有的路线都会删除无法恢复,确认继续操作吗?", @@ -21,6 +22,7 @@ "already-account": "已注册账户?", "altitude": "海拔", "amenity": "友好性", + "ammenity": "", "api-documentation": "API 文档", "api-tokens": "", "api-tokens-hint": "", @@ -263,6 +265,8 @@ "make-thumbnail": "生成缩略图", "map": "地图", "map-style": "地图样式", + "mark-trail-as-completed": "", + "mark-trail-as-completed-modal-text": "", "max-hiking-difficulty": "最大徒步难度", "metric": "公制", "moderate": "中等", @@ -290,6 +294,7 @@ "new-password-text": "设置新密码", "new-token-generated": "", "new-trail": "创建新路线", + "no": "", "no-account": "还未注册?", "no-api-tokens": "", "no-comments-so-far": "到目前为止没有评论", @@ -466,5 +471,6 @@ "welcome_to": "欢迎", "width": "宽度", "wrong-username-or-password": "用户名或密码无效", + "yes": "", "you-can": "你可以" } diff --git a/web/src/lib/models/api/trail_schema.ts b/web/src/lib/models/api/trail_schema.ts index f2f86de6..30d52a8e 100644 --- a/web/src/lib/models/api/trail_schema.ts +++ b/web/src/lib/models/api/trail_schema.ts @@ -9,6 +9,7 @@ const TrailCreateSchema = z.object({ location: z.string().optional(), date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"), public: z.boolean(), + completed: z.boolean(), difficulty: z.enum(["easy", "moderate", "difficult"]).optional(), lat: z.number().min(-90).max(90).optional(), lon: z.number().min(-180).max(180).optional(), @@ -32,6 +33,7 @@ const TrailUpdateSchema = z.object({ location: z.string().optional(), date: z.string().optional().refine((val) => !val || !isNaN(Date.parse(val)), "invalid-date"), public: z.boolean().optional(), + completed: z.boolean().optional(), difficulty: z.enum(["easy", "moderate", "difficult"]).optional(), lat: z.number().min(-90).max(90).optional(), lon: z.number().min(-180).max(180).optional(), diff --git a/web/src/lib/models/trail.ts b/web/src/lib/models/trail.ts index cbe2b474..4a405aeb 100644 --- a/web/src/lib/models/trail.ts +++ b/web/src/lib/models/trail.ts @@ -15,6 +15,7 @@ class Trail { location?: string; date?: string; public: boolean; + completed: boolean; distance?: number; elevation_gain?: number; elevation_loss?: number; @@ -55,6 +56,7 @@ class Trail { location?: string, date?: string, public?: boolean, + completed?: boolean, distance?: number, elevation_gain?: number, elevation_loss?: number, @@ -82,6 +84,7 @@ class Trail { this.location = params?.location; this.date = params?.date ?? new Date().toISOString().split('T')[0]; this.public = params?.public ?? false + this.completed = params?.completed ?? false, this.distance = params?.distance ?? 0; this.elevation_gain = params?.elevation_gain ?? 0; this.elevation_loss = params?.elevation_loss ?? 0; diff --git a/web/src/lib/stores/trail_store.ts b/web/src/lib/stores/trail_store.ts index 00fd8f37..6d04bad6 100644 --- a/web/src/lib/stores/trail_store.ts +++ b/web/src/lib/stores/trail_store.ts @@ -441,6 +441,7 @@ export async function searchResultToTrailList(hits: Hits): Pr name: h.name, photos: h.thumbnail ? [h.thumbnail] : [], public: h.public, + completed: h.completed, summit_logs: [], waypoints: [], tags: h.tags ?? [], diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte index e2adb90d..e7009a6b 100644 --- a/web/src/routes/trail/edit/[id]/+page.svelte +++ b/web/src/routes/trail/edit/[id]/+page.svelte @@ -101,6 +101,7 @@ import { z } from "zod"; import Track from "$lib/models/gpx/track.js"; import TrackSegment from "$lib/models/gpx/track-segment.js"; + import ConfirmModal from "$lib/components/confirm_modal.svelte"; let { data } = $props(); @@ -112,6 +113,7 @@ let waypointModal: WaypointModal; let summitLogModal: SummitLogModal; let listSelectModal: ListSearchModal; + let markTrailAsCompletedModal: ConfirmModal; let loading = $state(false); @@ -452,7 +454,8 @@ if (!$formData.expand!.waypoints_via_trail?.length) { $formData.expand!.waypoints_via_trail = []; } - $formData.expand!.waypoints_via_trail = $formData.expand!.waypoints_via_trail; + $formData.expand!.waypoints_via_trail = + $formData.expand!.waypoints_via_trail; // updateTrailOnMap(); } @@ -464,7 +467,8 @@ ) ?? -1; if (editedWaypointIndex >= 0) { - $formData.expand!.waypoints_via_trail![editedWaypointIndex] = savedWaypoint; + $formData.expand!.waypoints_via_trail![editedWaypointIndex] = + savedWaypoint; } else { savedWaypoint.id = cryptoRandomString({ length: 15 }); $formData.expand!.waypoints_via_trail = [ @@ -479,7 +483,9 @@ function moveMarker(marker: M.Marker, wpId?: string) { const position = marker.getLngLat(); const editableWaypointIndex = - $formData.expand!.waypoints_via_trail?.findIndex((w) => w.id == wpId) ?? -1; + $formData.expand!.waypoints_via_trail?.findIndex( + (w) => w.id == wpId, + ) ?? -1; const editableWaypoint = $formData.expand!.waypoints_via_trail![editableWaypointIndex]; if (!editableWaypoint) { @@ -487,7 +493,9 @@ } editableWaypoint.lat = position.lat; editableWaypoint.lon = position.lng; - $formData.expand!.waypoints_via_trail = [...($formData.expand!.waypoints_via_trail ?? [])]; + $formData.expand!.waypoints_via_trail = [ + ...($formData.expand!.waypoints_via_trail ?? []), + ]; // updateTrailOnMap(); } @@ -515,6 +523,13 @@ log, ]; } + + if ( + $formData.expand?.summit_logs_via_trail?.length == 1 && + !$formData.completed + ) { + markTrailAsCompletedModal.openModal(); + } } function handleSummitLogMenuClick( @@ -1167,6 +1182,10 @@ initRouteAnchors(valhallaStore.route, true); updateTrailWithRouteData(); } + + function markTrailAsCompleted() { + setFields("completed", true); + } @@ -1180,7 +1199,7 @@
+ handleListSelection(e)} > +