* initial commit * merge with origin/main * fix package-lock.json * fix tiptap mentions * remove flutter * update package-lock.json * disable tailwindcss/typography until https://github.com/tailwindlabs/tailwindcss/issues/19946 is fixed * reactivate tailwind/typography after fix --------- Co-authored-by: Christian Beutel <> Co-authored-by: slothful-vassal <89943360+slothful-vassal@users.noreply.github.com>
278 lines
9.4 KiB
Svelte
278 lines
9.4 KiB
Svelte
<script lang="ts">
|
|
import { goto } from "$app/navigation";
|
|
import { page } from "$app/state";
|
|
import emptyStateTrailDark from "$lib/assets/svgs/empty_states/empty_state_trail_dark.svg";
|
|
import emptyStateTrailLight from "$lib/assets/svgs/empty_states/empty_state_trail_light.svg";
|
|
import Search, {
|
|
type SearchItem,
|
|
} from "$lib/components/base/search.svelte";
|
|
import EmptyStateFeed from "$lib/components/empty_states/empty_state_feed.svelte";
|
|
import FeedCard from "$lib/components/profile/feed_card.svelte";
|
|
import Scene from "$lib/components/3D/scene.svelte";
|
|
import TrailCard from "$lib/components/trail/trail_card.svelte";
|
|
import {
|
|
defaultTrailSearchAttributes,
|
|
type TrailSearchResult,
|
|
} from "$lib/models/trail.js";
|
|
import { feed_index } from "$lib/stores/feed_store.js";
|
|
import {
|
|
searchActors,
|
|
searchMulti,
|
|
type ListSearchResult,
|
|
type LocationSearchResult,
|
|
} from "$lib/stores/search_store.js";
|
|
import { theme } from "$lib/stores/theme_store";
|
|
import { getIconForLocation } from "$lib/util/icon_util.js";
|
|
import { Canvas } from "@threlte/core";
|
|
import { marked } from "marked";
|
|
import { onMount, untrack } from "svelte";
|
|
import { _ } from "svelte-i18n";
|
|
import StarSky from "$lib/components/3D/star_sky.svelte";
|
|
import { fade } from "svelte/transition";
|
|
|
|
let { data } = $props();
|
|
|
|
let searchDropdownItems: SearchItem[] = $state([]);
|
|
|
|
let feed = $state(untrack(() => data.feed));
|
|
|
|
let pagination = $derived({
|
|
page: feed.page,
|
|
totalPages: feed.totalItems,
|
|
});
|
|
|
|
let loading: boolean = false;
|
|
|
|
let about: string = $state("");
|
|
|
|
onMount(async () => {
|
|
try {
|
|
const markdownResponse = await fetch("/md/about.md");
|
|
if (markdownResponse.ok) {
|
|
const text = await markdownResponse.text();
|
|
about = await marked.parse(text);
|
|
}
|
|
} catch (e) {
|
|
console.warn(e);
|
|
}
|
|
});
|
|
|
|
async function search(q: string) {
|
|
if (q.startsWith("@")) {
|
|
const actors = await searchActors(q);
|
|
searchDropdownItems = actors.map((a) => ({
|
|
text: a.username,
|
|
description: `@${a.preferred_username}${a.isLocal ? "" : "@" + a.domain}`,
|
|
value: a,
|
|
icon:
|
|
a.icon ||
|
|
`https://api.dicebear.com/7.x/initials/svg?seed=${a.preferred_username}&backgroundType=gradientLinear`,
|
|
}));
|
|
} else {
|
|
const r = await searchMulti({
|
|
queries: [
|
|
{
|
|
indexUid: "trails",
|
|
attributesToRetrieve: defaultTrailSearchAttributes,
|
|
q: q,
|
|
limit: 3,
|
|
},
|
|
{
|
|
indexUid: "lists",
|
|
q: q,
|
|
limit: 3,
|
|
},
|
|
{
|
|
indexUid: "locations",
|
|
q: q,
|
|
limit: 5,
|
|
},
|
|
],
|
|
});
|
|
const trailItems = r[0].hits.map((t: TrailSearchResult) => ({
|
|
text: t.name,
|
|
description: `Trail ${t.location.length ? ", " + t.location : ""}`,
|
|
value: `@${t.author_name}${t.domain ? `@${t.domain}` : ""}/${t.id}`,
|
|
icon: "route",
|
|
}));
|
|
const listItems = r[1].hits.map((t: ListSearchResult) => ({
|
|
text: t.name,
|
|
description: `List, ${t.trails} ${$_("trail", { values: { n: t.trails } })}`,
|
|
value: `@${t.author_name}${t.domain ? `@${t.domain}` : ""}/${t.id}`,
|
|
icon: "layer-group",
|
|
}));
|
|
const cityItems = r[2].hits.map((c: LocationSearchResult) => ({
|
|
text: c.name,
|
|
description: c.description,
|
|
value: c,
|
|
icon: getIconForLocation(c),
|
|
}));
|
|
|
|
searchDropdownItems = [...trailItems, ...listItems, ...cityItems];
|
|
}
|
|
}
|
|
|
|
function handleSearchClick(item: SearchItem) {
|
|
if (item.icon == "route") {
|
|
goto(`/trail/view/${item.value}`);
|
|
} else if (item.icon == "layer-group") {
|
|
goto(`/lists/${item.value}`);
|
|
} else if (item.value.preferred_username) {
|
|
goto(
|
|
`/profile/@${item.value.preferred_username}${item.value.isLocal ? "" : "@" + item.value.domain}`,
|
|
);
|
|
} else {
|
|
goto(`/map/?lat=${item.value.lat}&lon=${item.value.lon}`);
|
|
}
|
|
}
|
|
|
|
async function onScroll(e: Event) {
|
|
if (!page.data.user) {
|
|
return;
|
|
}
|
|
if (
|
|
window.innerHeight + window.scrollY >=
|
|
0.8 * document.body.offsetHeight &&
|
|
pagination.page !== pagination.totalPages &&
|
|
!loading
|
|
) {
|
|
loading = true;
|
|
await loadNextPage();
|
|
loading = false;
|
|
}
|
|
}
|
|
|
|
async function loadNextPage() {
|
|
pagination.page += 1;
|
|
feed = await feed_index(pagination.page);
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Home | wanderer</title>
|
|
</svelte:head>
|
|
|
|
<svelte:window onscroll={onScroll} />
|
|
|
|
<section
|
|
class="hero grid grid-cols-1 lg:grid-cols-2 lg:px-8 gap-4 lg:gap-8"
|
|
style="min-height: calc(100vh - 112px)"
|
|
>
|
|
<div
|
|
class="flex flex-col justify-center gap-8 px-8 lg:px-24 mt-0 lg:sticky"
|
|
style="max-height: calc(100vh - 112px); top: 112px;"
|
|
>
|
|
<h2 class="text-5xl sm:text-6xl lg:text-7xl font-bold">
|
|
{$_("welcome_to")} <span class="-tracking-[0.075em]">wanderer</span>
|
|
</h2>
|
|
<h5>
|
|
{$_("hero_section_0_text")}
|
|
</h5>
|
|
<Search
|
|
onupdate={search}
|
|
onclick={handleSearchClick}
|
|
large={true}
|
|
clearAfterSelect={false}
|
|
placeholder="{$_('search-for-trails-places')}..."
|
|
items={searchDropdownItems}
|
|
></Search>
|
|
</div>
|
|
{#if page.data.user}
|
|
<div class="space-y-2">
|
|
{#if feed.items.length === 0}
|
|
<EmptyStateFeed></EmptyStateFeed>
|
|
{/if}
|
|
{#each feed.items as f}
|
|
{#if f.expand?.item}
|
|
<FeedCard feedItem={f}></FeedCard>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
{/if}
|
|
</section>
|
|
{#if !page.data.user}
|
|
<div
|
|
class="hidden lg:block w-full fixed top-[112px] -z-10"
|
|
style="min-height: calc(100vh - 112px)"
|
|
>
|
|
<div class="invisible dark:visible">
|
|
<StarSky></StarSky>
|
|
</div>
|
|
<div class="absolute h-full w-[60%] left-[40%]">
|
|
<Canvas toneMapping={0}>
|
|
<Scene></Scene>
|
|
</Canvas>
|
|
{#if $theme === "light"}
|
|
<enhanced:img
|
|
class="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 max-h-3/4"
|
|
src="/static/models/wanderer_diorama_light.png"
|
|
alt="wanderer Diorama"
|
|
/>
|
|
{:else}
|
|
<enhanced:img
|
|
class="absolute top-1/2 -translate-y-1/2 left-1/2 -translate-x-1/2 max-h-3/4"
|
|
src="/static/models/wanderer_diorama_dark.png"
|
|
alt="wanderer Diorama"
|
|
/>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
<section class="lg:px-8 lg:max-w-1/2 mb-24" id="about">
|
|
<div class="px-8 lg:px-24">
|
|
<h2 class="text-4xl lg:text-5xl font-bold mt-1 mb-8">
|
|
{$_("about")}
|
|
</h2>
|
|
<div class="prose dark:prose-invert">
|
|
{@html about}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
<section class="lg:px-8 lg:max-w-1/2 mb-24" id="trails">
|
|
<div class="px-8 lg:px-24 space-y-4">
|
|
<h2 class="text-4xl lg:text-5xl font-bold">
|
|
{$_("explore-some-trails")}
|
|
</h2>
|
|
<h5>
|
|
{$_("hero_section_1_text")}
|
|
</h5>
|
|
{#if data.trails.length == 0}
|
|
<img
|
|
style="width: min(450px, 100%)"
|
|
class="rounded-md"
|
|
src={$theme === "light"
|
|
? emptyStateTrailLight
|
|
: emptyStateTrailDark}
|
|
alt="Empty state"
|
|
/>
|
|
{:else}
|
|
{#each data.trails as trail}
|
|
<a
|
|
class="w-full block"
|
|
href="/trail/view/@{trail.author}{trail.domain
|
|
? '@' + trail.domain
|
|
: ''}/{trail.id}"
|
|
>
|
|
<TrailCard
|
|
{trail}
|
|
fullWidth
|
|
selected={false}
|
|
hovered={false}
|
|
></TrailCard></a
|
|
>
|
|
{/each}
|
|
{/if}
|
|
</div>
|
|
</section>
|
|
<section id="get-started" class="lg:px-8 lg:max-w-1/2 mb-24">
|
|
<div class="px-8 lg:px-24 space-y-4 text-center">
|
|
<h2 class="text-4xl lg:text-5xl font-bold">{$_("get-started")}</h2>
|
|
<p>{$_("ready-to-join")}?</p>
|
|
<a
|
|
class="inline-block btn-primary btn-large"
|
|
href="/login"
|
|
role="button">Signup or Login</a
|
|
>
|
|
</div>
|
|
</section>
|
|
{/if}
|