your personal website on atproto - mirror
blento.app
1<script lang="ts">
2 import Card from '../cards/_base/Card/Card.svelte';
3 import Profile from './Profile.svelte';
4 import {
5 getDescription,
6 getHideProfileSection,
7 getProfilePosition,
8 getName,
9 sortItems,
10 getImage
11 } from '../helper';
12 import { innerWidth } from 'svelte/reactivity/window';
13 import { setDidContext, setHandleContext, setIsMobile } from './context';
14 import BaseCard from '../cards/_base/BaseCard/BaseCard.svelte';
15 import type { WebsiteData } from '$lib/types';
16 import Context from './Context.svelte';
17 import MadeWithBlento from './MadeWithBlento.svelte';
18 import Head from './Head.svelte';
19 import type { Did, Handle } from '@atcute/lexicons';
20 import QRModalProvider from '$lib/components/qr/QRModalProvider.svelte';
21 import ImageViewerProvider from '$lib/components/image-viewer/ImageViewerProvider.svelte';
22 import EmptyState from './EmptyState.svelte';
23 import FloatingEditButton from './FloatingEditButton.svelte';
24 import { user } from '$lib/atproto';
25 import { env } from '$env/dynamic/public';
26 import { page } from '$app/state';
27
28 let { data }: { data: WebsiteData } = $props();
29
30 // Check if floating edit button will be visible (to hide MadeWithBlento)
31 const isOwnPage = $derived(user.isLoggedIn && user.profile?.did === data.did);
32 const isBlento = $derived(!env.PUBLIC_IS_SELFHOSTED && data.handle === 'blento.app');
33 const isEditPage = $derived(page.url.pathname.endsWith('/edit'));
34 const showLoginOnEditPage = $derived(isEditPage && !user.isInitializing && !user.isLoggedIn);
35 const showFloatingButton = $derived(
36 (isOwnPage && !isEditPage) ||
37 showLoginOnEditPage ||
38 (isBlento && !user.isInitializing && !user.isLoggedIn) ||
39 (isBlento && user.isLoggedIn && user.profile?.handle !== data.handle)
40 );
41
42 let isMobile = $derived((innerWidth.current ?? 1000) < 1024);
43 setIsMobile(() => isMobile);
44
45 // svelte-ignore state_referenced_locally
46 setDidContext(data.did as Did);
47 // svelte-ignore state_referenced_locally
48 setHandleContext(data.handle as Handle);
49
50 let maxHeight = $derived(
51 data.cards.reduce(
52 (max, item) => Math.max(max, isMobile ? item.mobileY + item.mobileH : item.y + item.h),
53 0
54 )
55 );
56
57 let container: HTMLDivElement | undefined = $state();
58</script>
59
60<Head
61 favicon={getImage(data.publication, data.did, 'icon') || data.profile.avatar}
62 title={getName(data)}
63 image={'/' + data.handle + '/og.png'}
64 description={getDescription(data)}
65 accentColor={data.publication?.preferences?.accentColor}
66 baseColor={data.publication?.preferences?.baseColor}
67/>
68
69<Context {data}>
70 <QRModalProvider />
71 <ImageViewerProvider />
72 <div class="@container/wrapper relative w-full">
73 {#if !getHideProfileSection(data)}
74 <Profile {data} hideBlento={showFloatingButton} />
75 {/if}
76
77 <div
78 class={[
79 'mx-auto max-w-lg',
80 !getHideProfileSection(data) && getProfilePosition(data) === 'side'
81 ? '@5xl/wrapper:grid @5xl/wrapper:max-w-7xl @5xl/wrapper:grid-cols-4'
82 : '@5xl/wrapper:max-w-4xl'
83 ]}
84 >
85 <div></div>
86 <div bind:this={container} class="@container/grid relative col-span-3 px-2 py-8 lg:px-8">
87 {#if data.cards.length === 0 && data.page === 'blento.self'}
88 <EmptyState {data} />
89 {:else}
90 {#each data.cards.toSorted(sortItems) as item (item.id)}
91 <BaseCard {item}>
92 <Card {item} />
93 </BaseCard>
94 {/each}
95 <div style="height: {(maxHeight / 8) * 100}cqw;"></div>
96 {/if}
97 </div>
98 </div>
99
100 <MadeWithBlento class="mx-auto block pb-8 text-center @5xl/wrapper:hidden" />
101 </div>
102
103 <FloatingEditButton {data} />
104</Context>