diff --git a/web/src/lib/components/base/search.svelte b/web/src/lib/components/base/search.svelte index 9bd1ad4c..95ed659c 100644 --- a/web/src/lib/components/base/search.svelte +++ b/web/src/lib/components/base/search.svelte @@ -15,6 +15,7 @@ interface Props { maxSearchLength?: number; + timeBetweenUpdates?: number, value?: string; items?: SearchItem[]; placeholder?: string; @@ -29,6 +30,7 @@ let { maxSearchLength = 5, + timeBetweenUpdates = 500, value = $bindable(""), items = $bindable([]), placeholder = "Search...", @@ -57,7 +59,7 @@ } typingTimer = setTimeout(() => { update(value); - }, 500); + }, timeBetweenUpdates); } function update(q: string) { diff --git a/web/src/lib/components/list/list_search_modal.svelte b/web/src/lib/components/list/list_search_modal.svelte new file mode 100644 index 00000000..2f208016 --- /dev/null +++ b/web/src/lib/components/list/list_search_modal.svelte @@ -0,0 +1,243 @@ + + + + {#snippet children({ openModal })} + {@render children_render?.({ openModal })} + {/snippet} + {#snippet content()} +
+ updateLists(q)} + timeBetweenUpdates={0} + onclick={(item) => handleSelect(item.value)} + placeholder={$_("search-list")} + items={searchItems} + bind:value={query} + > + {#snippet prepend({ item })} + avatar + {/snippet} + + {#if query.trim().length && searchItems.length === 0} +

+ {$_("no-results")} +

+ {/if} + {#if listsContainingTrails.length} +
+

+ {$_("linked-lists")} +

+ {#each listsContainingTrails as list} +
+ avatar + +
+ {list.name} +
+ + +
+ {/each} +
+ {:else if showNoListHint} +
+

+ {$_("trail-not-in-list")} +

+
+ {/if} +
+ {/snippet} + {#snippet footer({ closeModal })} +
+ +
+ {/snippet} +
diff --git a/web/src/lib/components/list/list_select_modal.svelte b/web/src/lib/components/list/list_select_modal.svelte deleted file mode 100644 index ffb48d3e..00000000 --- a/web/src/lib/components/list/list_select_modal.svelte +++ /dev/null @@ -1,93 +0,0 @@ - - - - {#snippet children({ openModal })} - {@render children_render?.({ openModal })} - {/snippet} - {#snippet content()} - - {/snippet} - diff --git a/web/src/lib/components/trail/trail_dropdown.svelte b/web/src/lib/components/trail/trail_dropdown.svelte index c93ff61f..d34924a9 100644 --- a/web/src/lib/components/trail/trail_dropdown.svelte +++ b/web/src/lib/components/trail/trail_dropdown.svelte @@ -17,7 +17,7 @@ import { _ } from "svelte-i18n"; import Dropdown, { type DropdownItem } from "../base/dropdown.svelte"; import ConfirmModal from "../confirm_modal.svelte"; - import ListSelectModal from "../list/list_select_modal.svelte"; + import ListSearchModal from "../list/list_search_modal.svelte"; import TrailExportModal from "./trail_export_modal.svelte"; import TrailShareModal from "./trail_share_modal.svelte"; import { handleFromRecordWithIRI } from "$lib/util/activitypub_util"; @@ -35,7 +35,7 @@ let { trails, mode, toggle, onDelete, onShare }: Props = $props(); let confirmModal: ConfirmModal; - let listSelectModal: ListSelectModal; + let listSelectModal: ListSearchModal; let trailExportModal: TrailExportModal; let trailShareModal: TrailShareModal; @@ -436,12 +436,12 @@ bind:this={confirmModal} onconfirm={deleteTrails} > - handleListSelection(list)} -> +> exportTrails(settings)} diff --git a/web/src/lib/i18n/locales/de.json b/web/src/lib/i18n/locales/de.json index dbf7bb39..da31f7cc 100644 --- a/web/src/lib/i18n/locales/de.json +++ b/web/src/lib/i18n/locales/de.json @@ -11,7 +11,7 @@ "activity": "{n, plural, =1 {Aktivität} other {Aktivitäten}}", "add-bio": "Bio hinzufügen", "add-entry": "Eintrag hinzufügen", - "add-to-list": "Zu Liste hinzufügen", + "add-to-list": "Listen verwalten", "add-waypoint": "Wegpunkt hinzufügen", "added-trail-to": "Route hinzugefügt zu", "added-trails-to": "Routen hinzugefügt zu", @@ -220,6 +220,7 @@ "likes": "Likes", "limited": "Begrenzt", "link-copied": "Link kopiert!", + "linked-lists": "Verknüpfte Listen", "list": "{n, plural, =1 {Liste} other {Listen}}", "list-not-shared": "Mit niemandem geteilt", "list-public-warning": "Alle Routen in dieser Liste werden veröffentlicht.", @@ -403,7 +404,9 @@ "tourism": "Tourismus", "trail": "{n, plural, =1 {Route} other {Routen}}", "trail-not-shared": "Mit niemandem geteilt", + "trail-not-in-list": "Trail gehört zu keiner Liste.", "trail-saved-successfully": "Route gespeichert", + "search-list": "Liste suchen", "trails-for-you": "Routen für dich", "tram-stop": "Tram Haltestelle", "unchanged": "unverändert", diff --git a/web/src/lib/i18n/locales/en.json b/web/src/lib/i18n/locales/en.json index f2d89664..5bd1014e 100644 --- a/web/src/lib/i18n/locales/en.json +++ b/web/src/lib/i18n/locales/en.json @@ -11,7 +11,7 @@ "activity": "{n, plural, =1 {Activity} other {Activities}}", "add-bio": "Add Bio", "add-entry": "Add Entry", - "add-to-list": "Add to list", + "add-to-list": "Manage lists", "add-waypoint": "Add Waypoint", "added-trail-to": "Added trail to", "added-trails-to": "Added trails to", @@ -220,6 +220,7 @@ "likes": "Likes", "limited": "Limited", "link-copied": "Link copied!", + "linked-lists": "Linked lists", "list": "{n, plural, =1 {List} other {Lists}}", "list-not-shared": "Not shared with anyone", "list-public-warning": "All trails in this list will become public.", @@ -403,7 +404,9 @@ "tourism": "Tourism", "trail": "{n, plural, =1 {Trail} other {Trails}}", "trail-not-shared": "Not shared with anyone", + "trail-not-in-list": "Trail is not in any list", "trail-saved-successfully": "Trail saved successfully", + "search-list": "Search list", "trails-for-you": "Trails for you", "tram-stop": "Tram stop", "unchanged": "unchanged", diff --git a/web/src/lib/util/api_util.ts b/web/src/lib/util/api_util.ts index 78658403..dcb365f0 100644 --- a/web/src/lib/util/api_util.ts +++ b/web/src/lib/util/api_util.ts @@ -47,11 +47,12 @@ export enum Collection { export async function list(event: RequestEvent, collection: Collection) { const searchParams = Object.fromEntries(event.url.searchParams); const safeSearchParams = RecordListOptionsSchema.parse(searchParams); + const { perPage, page, ...opts } = safeSearchParams; let r: ListResult; if ((safeSearchParams.perPage ?? 0) < 0) { const activities: T[] = await event.locals.pb.collection(Collection[collection]) - .getFullList(safeSearchParams) + .getFullList({...opts}) r = { items: activities, perPage: -1, @@ -61,7 +62,7 @@ export async function list(event: RequestEvent, collection: Collection) { } } else { r = await event.locals.pb.collection(Collection[collection]) - .getList(safeSearchParams.page, safeSearchParams.perPage, { ...safeSearchParams }) + .getList(page, perPage, opts) } return r } @@ -163,4 +164,4 @@ export function handleError(e: any) { } else { return json({ message: e }, { status: 500 }) } -} \ No newline at end of file +} diff --git a/web/src/routes/trail/edit/[id]/+page.svelte b/web/src/routes/trail/edit/[id]/+page.svelte index 35298864..1a3d6498 100644 --- a/web/src/routes/trail/edit/[id]/+page.svelte +++ b/web/src/routes/trail/edit/[id]/+page.svelte @@ -5,7 +5,7 @@ import Select from "$lib/components/base/select.svelte"; import TextField from "$lib/components/base/text_field.svelte"; import Toggle from "$lib/components/base/toggle.svelte"; - import ListSelectModal from "$lib/components/list/list_select_modal.svelte"; + import ListSearchModal from "$lib/components/list/list_search_modal.svelte"; import SummitLogCard from "$lib/components/summit_log/summit_log_card.svelte"; import SummitLogModal from "$lib/components/summit_log/summit_log_modal.svelte"; import MapWithElevationMaplibre from "$lib/components/trail/map_with_elevation_maplibre.svelte"; @@ -111,7 +111,7 @@ let waypointModal: WaypointModal; let summitLogModal: SummitLogModal; - let listSelectModal: ListSelectModal; + let listSelectModal: ListSearchModal; let loading = $state(false); @@ -1516,11 +1516,11 @@ saveSummitLog(log)} > - handleListSelection(e)} -> +>