From c38d41546034fd3840c60d5c683e77429cfc0578 Mon Sep 17 00:00:00 2001 From: Christian Beutel <> Date: Fri, 13 Sep 2024 20:06:46 +0200 Subject: [PATCH] adds trail share --- .../1726231220_created_list_share.go | 96 ++++++ .../1726232380_updated_list_share.go | 50 +++ db/migrations/1726232603_updated_lists.go | 46 +++ .../1726245829_updated_trails_filter.go | 300 ++++++++++++++++++ web/src/lib/components/confirm_modal.svelte | 3 +- web/src/lib/components/list/list_card.svelte | 44 ++- .../components/list/list_share_modal.svelte | 222 +++++++++++++ ...il_share_info.svelte => share_info.svelte} | 26 +- .../lib/components/trail/trail_card.svelte | 4 +- .../components/trail/trail_info_panel.svelte | 4 +- .../components/trail/trail_list_item.svelte | 4 +- .../components/trail/trail_share_modal.svelte | 2 +- web/src/lib/i18n/locales/de.json | 7 + web/src/lib/i18n/locales/en.json | 7 + web/src/lib/i18n/locales/fr.json | 7 + web/src/lib/i18n/locales/hu.json | 7 + web/src/lib/i18n/locales/it.json | 7 + web/src/lib/i18n/locales/nl.json | 7 + web/src/lib/i18n/locales/pl.json | 7 + web/src/lib/i18n/locales/pt.json | 7 + web/src/lib/i18n/locales/zh.json | 7 + web/src/lib/models/list.ts | 3 + web/src/lib/models/list_share.ts | 17 + web/src/lib/stores/list_share_store.ts | 57 ++++ web/src/lib/stores/trail_share_store.ts | 8 +- .../libs/leaflet-gpxgroup.js | 15 +- web/src/routes/api/v1/list-share/+server.ts | 36 +++ .../routes/api/v1/list-share/[id]/+server.ts | 33 ++ web/src/routes/api/v1/list/+server.ts | 2 +- web/src/routes/api/v1/list/[id]/+server.ts | 2 +- web/src/routes/lists/+page.svelte | 62 ++-- web/src/routes/lists/edit/[id]/+page.svelte | 140 ++++++-- web/src/routes/lists/edit/[id]/+page.ts | 7 +- web/src/routes/map/trail/[id]/+page.svelte | 2 +- 34 files changed, 1144 insertions(+), 104 deletions(-) create mode 100644 db/migrations/1726231220_created_list_share.go create mode 100644 db/migrations/1726232380_updated_list_share.go create mode 100644 db/migrations/1726232603_updated_lists.go create mode 100644 db/migrations/1726245829_updated_trails_filter.go create mode 100644 web/src/lib/components/list/list_share_modal.svelte rename web/src/lib/components/{trail/trail_share_info.svelte => share_info.svelte} (87%) create mode 100644 web/src/lib/models/list_share.ts create mode 100644 web/src/lib/stores/list_share_store.ts create mode 100644 web/src/routes/api/v1/list-share/+server.ts create mode 100644 web/src/routes/api/v1/list-share/[id]/+server.ts diff --git a/db/migrations/1726231220_created_list_share.go b/db/migrations/1726231220_created_list_share.go new file mode 100644 index 00000000..3330ab83 --- /dev/null +++ b/db/migrations/1726231220_created_list_share.go @@ -0,0 +1,96 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/models" +) + +func init() { + m.Register(func(db dbx.Builder) error { + jsonData := `{ + "id": "1kot7t9na3hi0gl", + "created": "2024-09-13 12:40:20.308Z", + "updated": "2024-09-13 12:40:20.308Z", + "name": "list_share", + "type": "base", + "system": false, + "schema": [ + { + "system": false, + "id": "luqrtipy", + "name": "list", + "type": "relation", + "required": true, + "presentable": false, + "unique": false, + "options": { + "collectionId": "r6gu2ajyidy1x69", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "mix12kkh", + "name": "user", + "type": "relation", + "required": true, + "presentable": false, + "unique": false, + "options": { + "collectionId": "_pb_users_auth_", + "cascadeDelete": false, + "minSelect": null, + "maxSelect": 1, + "displayFields": null + } + }, + { + "system": false, + "id": "n9rjdx5g", + "name": "permission", + "type": "select", + "required": true, + "presentable": false, + "unique": false, + "options": { + "maxSelect": 1, + "values": [ + "view", + "edit" + ] + } + } + ], + "indexes": [], + "listRule": null, + "viewRule": null, + "createRule": null, + "updateRule": null, + "deleteRule": null, + "options": {} + }` + + collection := &models.Collection{} + if err := json.Unmarshal([]byte(jsonData), &collection); err != nil { + return err + } + + return daos.New(db).SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("1kot7t9na3hi0gl") + if err != nil { + return err + } + + return dao.DeleteCollection(collection) + }) +} diff --git a/db/migrations/1726232380_updated_list_share.go b/db/migrations/1726232380_updated_list_share.go new file mode 100644 index 00000000..5b70b9d0 --- /dev/null +++ b/db/migrations/1726232380_updated_list_share.go @@ -0,0 +1,50 @@ +package migrations + +import ( + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("1kot7t9na3hi0gl") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("list.author = @request.auth.id || user = @request.auth.id") + + collection.ViewRule = types.Pointer("list.author = @request.auth.id || user = @request.auth.id") + + collection.CreateRule = types.Pointer("list.author = @request.auth.id") + + collection.UpdateRule = types.Pointer("list.author = @request.auth.id") + + collection.DeleteRule = types.Pointer("list.author = @request.auth.id") + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("1kot7t9na3hi0gl") + if err != nil { + return err + } + + collection.ListRule = nil + + collection.ViewRule = nil + + collection.CreateRule = nil + + collection.UpdateRule = nil + + collection.DeleteRule = nil + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1726232603_updated_lists.go b/db/migrations/1726232603_updated_lists.go new file mode 100644 index 00000000..c5433c4b --- /dev/null +++ b/db/migrations/1726232603_updated_lists.go @@ -0,0 +1,46 @@ +package migrations + +import ( + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/tools/types" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("r6gu2ajyidy1x69") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("author = @request.auth.id || (@request.auth.id != \"\" && list_share_via_list.user ?= @request.auth.id)") + + collection.ViewRule = types.Pointer("author = @request.auth.id || (@request.auth.id != \"\" && list_share_via_list.user ?= @request.auth.id)") + + collection.CreateRule = types.Pointer("@request.auth.id != \"\" && (@request.data.author = @request.auth.id)") + + collection.UpdateRule = types.Pointer("author = @request.auth.id || (@request.auth.id != \"\" && list_share_via_list.list = id && list_share_via_list.user ?= @request.auth.id && list_share_via_list.permission = \"edit\")") + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("r6gu2ajyidy1x69") + if err != nil { + return err + } + + collection.ListRule = types.Pointer("author = @request.auth.id ") + + collection.ViewRule = types.Pointer("author = @request.auth.id ") + + collection.CreateRule = types.Pointer("author = @request.auth.id ") + + collection.UpdateRule = types.Pointer("author = @request.auth.id ") + + return dao.SaveCollection(collection) + }) +} diff --git a/db/migrations/1726245829_updated_trails_filter.go b/db/migrations/1726245829_updated_trails_filter.go new file mode 100644 index 00000000..5850544a --- /dev/null +++ b/db/migrations/1726245829_updated_trails_filter.go @@ -0,0 +1,300 @@ +package migrations + +import ( + "encoding/json" + + "github.com/pocketbase/dbx" + "github.com/pocketbase/pocketbase/daos" + m "github.com/pocketbase/pocketbase/migrations" + "github.com/pocketbase/pocketbase/models/schema" +) + +func init() { + m.Register(func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4wbv9tz5zjdrjh1") + if err != nil { + return err + } + + options := map[string]any{} + if err := json.Unmarshal([]byte(`{ + "query": "SELECT users.id, COALESCE(MAX(trails.distance), 0) AS max_distance, COALESCE(MAX(trails.elevation_gain), 0) AS max_elevation_gain, COALESCE(MAX(trails.duration), 0) AS max_duration, COALESCE(MIN(trails.distance), 0) AS min_distance, COALESCE(MIN(trails.elevation_gain), 0) AS min_elevation_gain, COALESCE(MIN(trails.duration), 0) AS min_duration FROM users LEFT JOIN trails ON users.id = trails.author OR trails.public = 1 OR EXISTS (\n SELECT 1 \n FROM trail_share \n WHERE trail_share.trail = trails.id \n AND trail_share.user = users.id\n ) GROUP BY users.id;" + }`), &options); err != nil { + return err + } + collection.SetOptions(options) + + // remove + collection.Schema.RemoveField("6tuhuumw") + + // remove + collection.Schema.RemoveField("xarhom23") + + // remove + collection.Schema.RemoveField("kl9wsxwf") + + // remove + collection.Schema.RemoveField("ipd1ohgc") + + // remove + collection.Schema.RemoveField("o4s7acfv") + + // remove + collection.Schema.RemoveField("d9lfg9vd") + + // add + new_max_distance := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "ixfa8u8m", + "name": "max_distance", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_max_distance); err != nil { + return err + } + collection.Schema.AddField(new_max_distance) + + // add + new_max_elevation_gain := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "bhn0jj40", + "name": "max_elevation_gain", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_max_elevation_gain); err != nil { + return err + } + collection.Schema.AddField(new_max_elevation_gain) + + // add + new_max_duration := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "3kujiwzh", + "name": "max_duration", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_max_duration); err != nil { + return err + } + collection.Schema.AddField(new_max_duration) + + // add + new_min_distance := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "yf3zwzn4", + "name": "min_distance", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_min_distance); err != nil { + return err + } + collection.Schema.AddField(new_min_distance) + + // add + new_min_elevation_gain := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "skvtkith", + "name": "min_elevation_gain", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_min_elevation_gain); err != nil { + return err + } + collection.Schema.AddField(new_min_elevation_gain) + + // add + new_min_duration := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "gydvo1ug", + "name": "min_duration", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), new_min_duration); err != nil { + return err + } + collection.Schema.AddField(new_min_duration) + + return dao.SaveCollection(collection) + }, func(db dbx.Builder) error { + dao := daos.New(db); + + collection, err := dao.FindCollectionByNameOrId("4wbv9tz5zjdrjh1") + if err != nil { + return err + } + + options := map[string]any{} + if err := json.Unmarshal([]byte(`{ + "query": "SELECT users.id, COALESCE(MAX(trails.distance), 0) AS max_distance, COALESCE(MAX(trails.elevation_gain), 0) AS max_elevation_gain, COALESCE(MAX(trails.duration), 0) AS max_duration, COALESCE(MIN(trails.distance), 0) AS min_distance, COALESCE(MIN(trails.elevation_gain), 0) AS min_elevation_gain, COALESCE(MIN(trails.duration), 0) AS min_duration FROM users LEFT JOIN trails ON users.id = trails.author OR trails.public = 1 OR EXISTS (\n SELECT 1 \n FROM trail_share \n WHERE trail_share.trail = trails.id \n AND trail_share.user = users.id\n );" + }`), &options); err != nil { + return err + } + collection.SetOptions(options) + + // add + del_max_distance := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "6tuhuumw", + "name": "max_distance", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_max_distance); err != nil { + return err + } + collection.Schema.AddField(del_max_distance) + + // add + del_max_elevation_gain := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "xarhom23", + "name": "max_elevation_gain", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_max_elevation_gain); err != nil { + return err + } + collection.Schema.AddField(del_max_elevation_gain) + + // add + del_max_duration := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "kl9wsxwf", + "name": "max_duration", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_max_duration); err != nil { + return err + } + collection.Schema.AddField(del_max_duration) + + // add + del_min_distance := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "ipd1ohgc", + "name": "min_distance", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_min_distance); err != nil { + return err + } + collection.Schema.AddField(del_min_distance) + + // add + del_min_elevation_gain := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "o4s7acfv", + "name": "min_elevation_gain", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_min_elevation_gain); err != nil { + return err + } + collection.Schema.AddField(del_min_elevation_gain) + + // add + del_min_duration := &schema.SchemaField{} + if err := json.Unmarshal([]byte(`{ + "system": false, + "id": "d9lfg9vd", + "name": "min_duration", + "type": "json", + "required": false, + "presentable": false, + "unique": false, + "options": { + "maxSize": 1 + } + }`), del_min_duration); err != nil { + return err + } + collection.Schema.AddField(del_min_duration) + + // remove + collection.Schema.RemoveField("ixfa8u8m") + + // remove + collection.Schema.RemoveField("bhn0jj40") + + // remove + collection.Schema.RemoveField("3kujiwzh") + + // remove + collection.Schema.RemoveField("yf3zwzn4") + + // remove + collection.Schema.RemoveField("skvtkith") + + // remove + collection.Schema.RemoveField("gydvo1ug") + + return dao.SaveCollection(collection) + }) +} diff --git a/web/src/lib/components/confirm_modal.svelte b/web/src/lib/components/confirm_modal.svelte index e54ee683..14d6616f 100644 --- a/web/src/lib/components/confirm_modal.svelte +++ b/web/src/lib/components/confirm_modal.svelte @@ -8,6 +8,7 @@ export let title: string = "Confirm Deletion"; export let text: string; + export let action: string = "delete"; const dispatch = createEventDispatcher(); @@ -24,7 +25,7 @@ >{$_("cancel")} {$_(action)} diff --git a/web/src/lib/components/list/list_card.svelte b/web/src/lib/components/list/list_card.svelte index 90016736..f35e51f0 100644 --- a/web/src/lib/components/list/list_card.svelte +++ b/web/src/lib/components/list/list_card.svelte @@ -8,14 +8,11 @@ } from "$lib/util/format_util"; import Dropdown, { type DropdownItem } from "../base/dropdown.svelte"; import { _ } from "svelte-i18n"; + import ShareInfo from "../share_info.svelte"; + import { currentUser } from "$lib/stores/user_store"; export let list: List; export let active: boolean = false; - const dropdownItems: DropdownItem[] = [ - { text: $_("edit"), value: "edit" }, - { text: $_("delete"), value: "delete" }, - ]; - $: cumulativeDistance = list.expand?.trails.reduce( (s, b) => s + b.distance!, 0, @@ -30,6 +27,22 @@ (s, b) => s + b.duration!, 0, ); + + $: listIsShared = (list.expand?.list_share_via_list?.length ?? 0) > 0; + + $: allowEdit = + list.author == $currentUser?.id || + list.expand?.list_share_via_list?.some((s) => s.permission == "edit"); + + $: dropdownItems = [ + ...(list.author == $currentUser?.id + ? [{ text: $_("share"), value: "share" }] + : []), + ...(allowEdit ? [{ text: $_("edit"), value: "edit" }] : []), + ...(list.author == $currentUser?.id + ? [{ text: $_("delete"), value: "delete" }] + : []), + ];
{list.expand?.trails.length ?? 0} +
+ {list.expand?.trails.length ?? 0} {$_("trail", { values: { n: list.expand?.trails.length ?? 0 }, - })}
+ })} +
+ import Modal from "$lib/components/base/modal.svelte";
+ import type { List } from "$lib/models/list";
+ import { ListShare } from "$lib/models/list_share";
+ import { TrailShare } from "$lib/models/trail_share";
+ import type { User } from "$lib/models/user";
+ import {
+ list_share_create,
+ list_share_delete,
+ list_share_index,
+ list_share_update,
+ shares,
+ } from "$lib/stores/list_share_store";
+ import { show_toast } from "$lib/stores/toast_store";
+ import {
+ trail_share_create,
+ trail_share_delete,
+ trail_share_index,
+ } from "$lib/stores/trail_share_store";
+ import { users_search } from "$lib/stores/user_store";
+ import { getFileURL } from "$lib/util/file_util";
+ import { createEventDispatcher } from "svelte";
+ import { _ } from "svelte-i18n";
+ import Button from "../base/button.svelte";
+ import Search, { type SearchItem } from "../base/search.svelte";
+ import type { SelectItem } from "../base/select.svelte";
+ import Select from "../base/select.svelte";
+ import { useFBO } from "@threlte/extras";
+ import { lists } from "$lib/stores/list_store";
+
+ let openModal: (() => void) | undefined = undefined;
+ export let closeModal: (() => void) | undefined = undefined;
+ export function openShareModal() {
+ shares.set([]);
+ fetchShares();
+ if (openModal) {
+ openModal();
+ }
+ }
+
+ export let list: List;
+
+ const dispatch = createEventDispatcher();
+
+ let copyButtonText = $_("copy-link");
+
+ let searchItems: SearchItem[] = [];
+
+ let sharesLoading: boolean = false;
+
+ const permissionSelectItems: SelectItem[] = [
+ { text: $_("view"), value: "view" },
+ { text: $_("edit"), value: "edit" },
+ ];
+
+ async function updateUsers(q: string) {
+ try {
+ const users: User[] = await users_search(q);
+ searchItems = users.map((u) => ({
+ text: u.username!,
+ value: u,
+ icon: "user",
+ }));
+ } catch (e) {
+ console.error(e);
+ show_toast({
+ type: "error",
+ icon: "close",
+ text: "Error during search",
+ });
+ }
+ }
+
+ function copyURLToClipboard() {
+ navigator.clipboard.writeText(window.location.href);
+
+ copyButtonText = $_("link-copied");
+ setTimeout(() => (copyButtonText = $_("copy-link")), 3000);
+ }
+
+ function close() {
+ searchItems = [];
+ dispatch("save");
+ closeModal!();
+ }
+
+ async function shareTrails(userId: string) {
+ const existingTrailShares = await trail_share_index({ user: userId });
+ const trailIds = existingTrailShares.map((s) => s.trail);
+ for (const trailId of list.trails ?? []) {
+ if (!trailIds.includes(trailId)) {
+ const share = new TrailShare(userId, trailId, "view");
+ await trail_share_create(share);
+ }
+ }
+ }
+
+ async function shareList(item: SelectItem) {
+ const share = new ListShare(item.value.id, list.id!, "view");
+ await list_share_create(share);
+ await shareTrails(item.value.id);
+ fetchShares();
+ }
+
+ async function updateSharePermission(
+ share: ListShare,
+ permission: "view" | "edit",
+ ) {
+ share.permission = permission;
+ await list_share_update(share);
+ }
+
+ async function deleteTrailShares(userId: string) {
+ const existingTrailShares = await trail_share_index({ user: userId });
+ for (const trailId of list.trails ?? []) {
+ const shareToDelete = existingTrailShares.find(
+ (s) => s.trail == trailId,
+ );
+ if (shareToDelete) {
+ await trail_share_delete(shareToDelete);
+ }
+ }
+ }
+
+ async function deleteShare(share: ListShare) {
+ await list_share_delete(share);
+ // await deleteTrailShares(share.user);
+ fetchShares();
+ }
+
+ async function fetchShares() {
+ sharesLoading = true;
+ const fetchedShares = await list_share_index(list.id!);
+ list.expand = {
+ trails: list.expand?.trails ?? [],
+ list_share_via_list: fetchedShares,
+ };
+ sharesLoading = false;
+ }
+
+
+
+ {$_("list-share-warning")}
+
+ {$_("list-not-shared")}
+ {share.expand.user.username}
+
{$_("shared-with")}
+
+ {#if $shares.length == 0}
+
+
({
- expand: "trails,trails.waypoints,trails.category",
+ expand: "trails,trails.waypoints,trails.category,list_share_via_list",
sort: sort,
})
return json(r)
diff --git a/web/src/routes/api/v1/list/[id]/+server.ts b/web/src/routes/api/v1/list/[id]/+server.ts
index 793fe23a..52564b04 100644
--- a/web/src/routes/api/v1/list/[id]/+server.ts
+++ b/web/src/routes/api/v1/list/[id]/+server.ts
@@ -5,7 +5,7 @@ import { error, json, type RequestEvent } from "@sveltejs/kit";
export async function GET(event: RequestEvent) {
try {
const r = await pb.collection('lists')
- .getOne
(event.params.id as string, { expand: "trails,trails.waypoints,trails.category" })
+ .getOne
(event.params.id as string, { expand: "trails,trails.waypoints,trails.category,list_share_via_list" })
return json(r)
} catch (e: any) {
throw error(e.status, e);
diff --git a/web/src/routes/lists/+page.svelte b/web/src/routes/lists/+page.svelte
index 86aafdcc..62d38b54 100644
--- a/web/src/routes/lists/+page.svelte
+++ b/web/src/routes/lists/+page.svelte
@@ -5,6 +5,7 @@
import ConfirmModal from "$lib/components/confirm_modal.svelte";
import ListCard from "$lib/components/list/list_card.svelte";
import ListModal from "$lib/components/list/list_modal.svelte";
+ import ListShareModal from "$lib/components/list/list_share_modal.svelte";
import MapWithElevationMultiple from "$lib/components/trail/map_with_elevation_multiple.svelte";
import TrailList from "$lib/components/trail/trail_list.svelte";
import { List } from "$lib/models/list";
@@ -28,6 +29,7 @@
let openListModal: () => void;
let openConfirmModal: () => void;
+ let openShareModal: () => void;
let filter: TrailFilter = $page.data.filter;
let listToBeDeleted: List | null = null;
@@ -60,7 +62,11 @@
) {
const item = e.detail;
- if (item.value == "edit") {
+ if (item.value == "share") {
+ list.set(currentList);
+ await tick();
+ openShareModal();
+ } else if (item.value == "edit") {
goto("/lists/edit/" + currentList.id);
} else if (item.value == "delete") {
openConfirmModal();
@@ -91,19 +97,14 @@