tangled
alpha
login
or
join now
flo-bit.dev
/
blento
your personal website on atproto - mirror
blento.app
19
fork
atom
overview
issues
pulls
pipelines
add standard site document list, small fixes
Florian
2 weeks ago
f4e2e6c7
dbe2f418
+147
-16
9 changed files
expand all
collapse all
unified
split
src
lib
cards
StandardSiteDocumentListCard
BlogEntry.svelte
DateTime.svelte
StandardSiteDocumentListCard.svelte
index.ts
TealFMPlaysCard
TealFMPlaysCard.svelte
index.ts
website
Account.svelte
EditBar.svelte
load.ts
+32
src/lib/cards/StandardSiteDocumentListCard/BlogEntry.svelte
···
1
1
+
<script lang="ts">
2
2
+
import { RelativeTime } from '@foxui/time';
3
3
+
import DateTime from './DateTime.svelte';
4
4
+
5
5
+
let {
6
6
+
title,
7
7
+
date,
8
8
+
description,
9
9
+
href
10
10
+
}: { title: string; date: string; description: string; href: string } = $props();
11
11
+
</script>
12
12
+
13
13
+
<article class="group/article relative isolate flex flex-col">
14
14
+
<div class={'text-base-500 accent:text-accent-950 flex shrink-0 items-center gap-x-4 text-xs'}>
15
15
+
<DateTime date={new Date(date)} />
16
16
+
</div>
17
17
+
<div class="max-w-xl">
18
18
+
<div class="text-base-900 dark:text-base-50 mt-3 text-lg leading-6 font-semibold">
19
19
+
<a {href} target="_blank">
20
20
+
<span class="absolute inset-0"></span>
21
21
+
{title}
22
22
+
</a>
23
23
+
24
24
+
<div
25
25
+
class="bg-base-200/30 accent:bg-accent-200/20 dark:bg-base-800/30 absolute -inset-2 -z-10 scale-95 rounded-2xl opacity-0 transition-all duration-150 group-hover/article:scale-100 group-hover/article:opacity-100"
26
26
+
></div>
27
27
+
</div>
28
28
+
<p class="text-base-600 dark:text-base-400 accent:text-base-800 mt-5 text-sm leading-6">
29
29
+
{description}
30
30
+
</p>
31
31
+
</div>
32
32
+
</article>
+11
src/lib/cards/StandardSiteDocumentListCard/DateTime.svelte
···
1
1
+
<script lang="ts">
2
2
+
let { date }: { date: Date } = $props();
3
3
+
</script>
4
4
+
5
5
+
<time datetime={date.toISOString()}>
6
6
+
{date.toLocaleDateString('en-us', {
7
7
+
year: 'numeric',
8
8
+
month: 'short',
9
9
+
day: 'numeric'
10
10
+
})}
11
11
+
</time>
+41
src/lib/cards/StandardSiteDocumentListCard/StandardSiteDocumentListCard.svelte
···
1
1
+
<script lang="ts">
2
2
+
import { getAdditionalUserData, getDidContext, getHandleContext } from '$lib/website/context';
3
3
+
import { onMount } from 'svelte';
4
4
+
import { CardDefinitionsByType } from '..';
5
5
+
import type { ContentComponentProps } from '../types';
6
6
+
import BlogEntry from './BlogEntry.svelte';
7
7
+
8
8
+
let { item }: ContentComponentProps = $props();
9
9
+
10
10
+
const data = getAdditionalUserData();
11
11
+
// svelte-ignore state_referenced_locally
12
12
+
let feed = $state(data[item.cardType] as any);
13
13
+
14
14
+
let did = getDidContext();
15
15
+
let handle = getHandleContext();
16
16
+
17
17
+
onMount(async () => {
18
18
+
console.log(feed);
19
19
+
if (!feed) {
20
20
+
feed = (await CardDefinitionsByType[item.cardType]?.loadData?.([item], {
21
21
+
did,
22
22
+
handle
23
23
+
})) as any;
24
24
+
25
25
+
console.log(feed);
26
26
+
27
27
+
data[item.cardType] = feed;
28
28
+
}
29
29
+
});
30
30
+
</script>
31
31
+
32
32
+
<div class="flex h-full flex-col gap-10 overflow-y-scroll p-8">
33
33
+
{#each feed ?? [] as document}
34
34
+
<BlogEntry
35
35
+
title={document.value.title}
36
36
+
description={document.value.description}
37
37
+
date={document.value.publishedAt}
38
38
+
href={document.value.href}
39
39
+
/>
40
40
+
{/each}
41
41
+
</div>
+41
src/lib/cards/StandardSiteDocumentListCard/index.ts
···
1
1
+
import { getRecord, listRecords } from '$lib/oauth/atproto';
2
2
+
import { parseUri } from '$lib/oauth/utils';
3
3
+
import type { CardDefinition } from '../types';
4
4
+
import StandardSiteDocumentListCard from './StandardSiteDocumentListCard.svelte';
5
5
+
6
6
+
export const StandardSiteDocumentListCardDefinition = {
7
7
+
type: 'publicationList',
8
8
+
contentComponent: StandardSiteDocumentListCard,
9
9
+
createNew: (card) => {
10
10
+
card.w = 4;
11
11
+
card.mobileW = 8;
12
12
+
card.mobileH = 6;
13
13
+
},
14
14
+
15
15
+
loadData: async (items, { did }) => {
16
16
+
const records = await listRecords({ did, collection: 'site.standard.document' });
17
17
+
18
18
+
const publications: Record<string, string> = {};
19
19
+
for (const record of records) {
20
20
+
const site = record.value.site as string;
21
21
+
22
22
+
if (site.startsWith('at://')) {
23
23
+
if (!publications[site]) {
24
24
+
const siteParts = parseUri(site);
25
25
+
26
26
+
const publicationRecord = await getRecord(siteParts);
27
27
+
28
28
+
publications[site] = publicationRecord.value.url as string;
29
29
+
}
30
30
+
31
31
+
record.value.href = publications[site] + record.value.path;
32
32
+
} else {
33
33
+
record.value.href = site + record.value.path;
34
34
+
}
35
35
+
}
36
36
+
37
37
+
return records;
38
38
+
},
39
39
+
40
40
+
sidebarButtonText: 'site.standard.document list'
41
41
+
} as CardDefinition & { type: 'site.standard.document list' };
+10
-13
src/lib/cards/TealFMPlaysCard/TealFMPlaysCard.svelte
···
16
16
let handle = getHandleContext();
17
17
18
18
onMount(async () => {
19
19
-
console.log(feed);
20
20
-
if (!feed) {
21
21
-
feed = (await CardDefinitionsByType[item.cardType]?.loadData?.([], {
22
22
-
did,
23
23
-
handle
24
24
-
})) as any;
19
19
+
if (feed) return;
25
20
26
26
-
console.log(feed);
21
21
+
feed = (await CardDefinitionsByType[item.cardType]?.loadData?.([], {
22
22
+
did,
23
23
+
handle
24
24
+
})) as any;
27
25
28
28
-
data[item.cardType] = feed;
29
29
-
}
26
26
+
data[item.cardType] = feed;
30
27
});
31
28
32
29
function isNumeric(str: string) {
33
33
-
if (typeof str != 'string') return false; // we only process strings!
30
30
+
if (typeof str != 'string') return false;
34
31
return (
35
35
-
!isNaN(str) && // use type coercion to parse the _entirety_ of the string (`parseFloat` alone does not do this)...
32
32
+
!isNaN(str) &&
36
33
!isNaN(parseFloat(str))
37
37
-
); // ...and ensure strings of whitespace fail
34
34
+
);
38
35
}
39
36
</script>
40
37
···
74
71
{/snippet}
75
72
76
73
<div class="z-10 flex h-full w-full flex-col gap-4 overflow-y-scroll p-4">
77
77
-
{#each (feed ?? []).slice(0, 20) as play}
74
74
+
{#each feed ?? [] as play}
78
75
{#if play.value.originUrl}
79
76
<a href={play.value.originUrl} target="_blank" rel="noopener noreferrer" class="w-full">
80
77
{@render musicItem(play)}
+3
-1
src/lib/cards/index.ts
···
21
21
import { PopfeedReviewsCardDefinition } from './PopfeedReviews';
22
22
import { TealFMPlaysCardDefinition } from './TealFMPlaysCard';
23
23
import { PhotoGalleryCardDefinition } from './PhotoGalleryCard';
24
24
+
import { StandardSiteDocumentListCardDefinition } from './StandardSiteDocumentListCard';
24
25
25
26
export const AllCardDefinitions = [
26
27
ImageCardDefinition,
···
44
45
TetrisCardDefinition,
45
46
PopfeedReviewsCardDefinition,
46
47
TealFMPlaysCardDefinition,
47
47
-
PhotoGalleryCardDefinition
48
48
+
PhotoGalleryCardDefinition,
49
49
+
StandardSiteDocumentListCardDefinition
48
50
] as const;
49
51
50
52
export const CardDefinitionsByType = AllCardDefinitions.reduce(
+1
-1
src/lib/website/Account.svelte
···
24
24
<Button variant="ghost" onclick={logout}>Logout</Button>
25
25
</Popover>
26
26
</div>
27
27
-
{:else}
27
27
+
{:else if !client.isInitializing}
28
28
<div
29
29
class="dark:bg-base-950 border-base-200 dark:border-base-900 fixed top-4 right-4 z-20 flex flex-col gap-4 rounded-2xl border bg-white p-4 shadow-lg"
30
30
>
+1
-1
src/lib/website/EditBar.svelte
···
60
60
bind:this={videoInputRef}
61
61
/>
62
62
63
63
-
{#if client.isLoggedIn && client.profile?.did === data.did}
63
63
+
{#if dev || (client.isLoggedIn && client.profile?.did === data.did)}
64
64
<Navbar
65
65
class={[
66
66
'dark:bg-base-900 bg-base-100 top-auto bottom-2 mx-4 mt-3 max-w-3xl rounded-full px-4 md:mx-auto lg:inline-flex',
+7
src/lib/website/load.ts
···
17
17
const update = result.updatedAt;
18
18
const timePassed = (Date.now() - update) / 1000;
19
19
20
20
+
const ONE_DAY = 60 * 60 * 24;
21
21
+
20
22
if (!result.version || result.version !== CURRENT_CACHE_VERSION) {
21
23
console.log('skipping cache because of version mismatch');
24
24
+
return;
25
25
+
}
26
26
+
27
27
+
if (timePassed > ONE_DAY) {
28
28
+
console.log('skipping cache because of age');
22
29
return;
23
30
}
24
31