Federation (#327)
* initial commit federation * more federation * more federation * more federation * completes follow, accept, undo * add trail create activity * process trail create activity * more trail create activity * adds missing endpoints * adds update and delete activity * adds comment activities * adds activities back * adds summit logs activities * deletes follow counts table * fixes migrations * fixes migrations * fixes migrations * fixes migrations * adds remote profiles * ctd * ctd * we are getting closer... * adds public summit logs * fixes federated trails in lists * adds remote lists * adds list activites * fixes lists * adds notifications * adds iri redirect * fixes comments and summitlogs * fixes follows and profiles * adds asynchronous send * fixes list search * adds encryption key * bug fixes * fixes activity signing * removes custon activity object types * adds html editor * fixes html editor * fixes display issues on mastodon * adds federated sharing * finishes announcements * fixes small summit log issues * adds trail likes * finalizes likes * fixes images for komoot * add disable federation option * adds private profiles * updates docs * adds federated comments * adds trail and comments actvitiypub routes * adds mentions to editor * adds mentions to trails, comments, summit logs * updates theme * updates theme * update docs * update docs * updates docs * fixes various frontend problems * fixes activitypub follows api * updates docs * updates docs --------- Co-authored-by: Christian Beutel <>
This commit is contained in:
159
web/src/routes/profile/[handle]/+page.svelte
Normal file
159
web/src/routes/profile/[handle]/+page.svelte
Normal file
@@ -0,0 +1,159 @@
|
||||
<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 ActivityCard from "$lib/components/profile/activity_card.svelte";
|
||||
|
||||
import type { TimelineItem } from "$lib/models/timeline.js";
|
||||
import { profile_timeline_index } from "$lib/stores/profile_store.js";
|
||||
import { theme } from "$lib/stores/theme_store.js";
|
||||
import { getFileURL } from "$lib/util/file_util.js";
|
||||
import { HTML } from "@threlte/extras";
|
||||
import { _ } from "svelte-i18n";
|
||||
import { temp } from "three/examples/jsm/nodes/Nodes.js";
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let timeline = $derived(data.timeline);
|
||||
|
||||
let loading: boolean = false;
|
||||
|
||||
let pagination = $derived({
|
||||
page: 1,
|
||||
totalPages: data.timeline.totalItems,
|
||||
});
|
||||
|
||||
async function onListScroll(e: Event) {
|
||||
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;
|
||||
data.timeline = await profile_timeline_index(
|
||||
data.actor.iri,
|
||||
pagination.page,
|
||||
);
|
||||
}
|
||||
|
||||
function handleTimeLineItemClick(item: TimelineItem) {
|
||||
if (item.trail_iri.length) {
|
||||
const originalTrailId = item.trail_iri.substring(
|
||||
item.trail_iri.length - 15,
|
||||
);
|
||||
goto(
|
||||
`/trail/view/@${item.trail_author_username}@${item.trail_author_domain}/${item.trail_id}`,
|
||||
);
|
||||
} else {
|
||||
goto(`/trail/view/${page.params.handle}/${item.trail_id}`);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:window onscroll={onListScroll} />
|
||||
|
||||
<svelte:head>
|
||||
<title>{$_("profile")} | wanderer</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-4">
|
||||
<h4 class="text-xl font-semibold">
|
||||
{$_("about")}
|
||||
{data.profile.preferredUsername}
|
||||
{#if data.isOwnProfile && data.profile.bio.length}
|
||||
<a aria-label="Edit bio" class="ml-4" href="/settings/profile"
|
||||
><i class="fa fa-pen text-base"></i></a
|
||||
>
|
||||
{/if}
|
||||
</h4>
|
||||
{#if data.profile.bio.length}
|
||||
<p class="prose dark:prose-invert">{@html data.profile.bio}</p>
|
||||
{:else if data.isOwnProfile}
|
||||
<a class="btn-primary inline-block" href="/settings/profile"
|
||||
>+ {$_("add-bio")}</a
|
||||
>
|
||||
{:else}
|
||||
<p class="w-full text-center text-gray-500 text-sm">
|
||||
{$_("empty-bio", {
|
||||
values: { username: data.profile.preferredUsername },
|
||||
})}
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<h4 class="text-xl font-semibold">
|
||||
{$_("list", { values: { n: 2 } })}
|
||||
</h4>
|
||||
<div class="flex gap-x-4 overflow-x-scroll">
|
||||
{#if !data.lists.length && data.isOwnProfile}
|
||||
<a class="btn-primary inline-block" href="/lists/edit/new"
|
||||
>+ {$_("new-list")}</a
|
||||
>
|
||||
{:else if !data.lists.length}
|
||||
<p class="w-full text-center text-gray-500 text-sm">
|
||||
{$_("empty-lists", {
|
||||
values: { username: data.profile.preferredUsername },
|
||||
})}
|
||||
</p>
|
||||
{/if}
|
||||
{#each data.lists as list}
|
||||
<a
|
||||
href="/lists/{page.params.handle}/{list.id}"
|
||||
class="relative w-64 h-48 rounded-xl overflow-hidden group shrink-0"
|
||||
>
|
||||
<img
|
||||
class="w-full h-full object-cover transition-transform group-hover:scale-110"
|
||||
src={list.avatar
|
||||
? getFileURL(list, list.avatar)
|
||||
: $theme === "light"
|
||||
? emptyStateTrailLight
|
||||
: emptyStateTrailDark}
|
||||
alt="avatar"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="absolute bottom-0 w-full h-2/3 bg-gradient-to-b from-transparent to-black opacity-50"
|
||||
></div>
|
||||
<h5
|
||||
class="absolute bottom-4 left-4 font-semibold text-white"
|
||||
>
|
||||
{list.name}
|
||||
</h5>
|
||||
</a>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-4">
|
||||
<h4 class="text-xl font-semibold">Timeline</h4>
|
||||
{#if !timeline.items?.length && data.isOwnProfile}
|
||||
<a class="btn-primary inline-block" href="/trails/edit/new"
|
||||
>+ {$_("new-trail")}</a
|
||||
>
|
||||
{:else if !timeline.items?.length}
|
||||
<p class="w-full text-center text-gray-500 text-sm">
|
||||
{$_("empty-activities", {
|
||||
values: { username: data.profile.preferredUsername },
|
||||
})}
|
||||
</p>
|
||||
{/if}
|
||||
{#each timeline.items as item}
|
||||
<div
|
||||
class="py-1 cursor-pointer"
|
||||
role="presentation"
|
||||
onclick={() => handleTimeLineItemClick(item)}
|
||||
>
|
||||
<ActivityCard activity={item} actor={data.actor}></ActivityCard>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user