aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar buttercat1791 <mjjurkoic@gmail.com>2024-08-22 09:30:47 -0500
committerLibravatar limina1 <liminal@duck.com>2024-08-27 08:22:01 -0400
commita67ac58d5ed173f039be67d491db8d4f2a785884 (patch)
tree3bf5a9ef6887aae76d900ffbde90a96bb3c42ddc
parentf0567c160e2b2b3448fbe41a1eabeb953dd3829f (diff)
Filter for valid NKBIP-01 events
-rw-r--r--package.json2
-rw-r--r--src/lib/utils.ts25
-rw-r--r--src/routes/+layout.svelte19
-rw-r--r--src/routes/+page.svelte71
4 files changed, 85 insertions, 32 deletions
diff --git a/package.json b/package.json
index bec892a..d3b92a9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "alexandria",
- "version": "0.0.3",
+ "version": "0.0.4",
"private": true,
"type": "module",
"scripts": {
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
index c709bbd..67d6e08 100644
--- a/src/lib/utils.ts
+++ b/src/lib/utils.ts
@@ -1,5 +1,6 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools";
+
export function neventEncode(event: NDKEvent, relays: string[]) {
return nip19.neventEncode({
id: event.id,
@@ -81,3 +82,27 @@ export function isElementInViewport(el: string | HTMLElement) {
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
+
+/**
+ * Removes `kind: 30040` index events that don't comply with the NKBIP-01 specification.
+ * @param events A set of events.
+ * @returns The filtered set of events.
+ */
+export function filterValidIndexEvents(events: Set<NDKEvent>): Set<NDKEvent> {
+ // The filter object supports only limited parameters, so we need to filter out events that
+ // don't respect NKBIP-01.
+ events.forEach(event => {
+ // Index events have no content, and they must have `title`, `d`, and `e` tags.
+ if (
+ event.content != null
+ || event.getMatchingTags('title').length === 0
+ || event.getMatchingTags('d').length === 0
+ || event.getMatchingTags('e').length === 0
+ ) {
+ events.delete(event);
+ }
+ });
+
+ console.debug(`Filtered index events: ${events.size} events remaining.`);
+ return events;
+}
diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte
index dddce98..d8c30ea 100644
--- a/src/routes/+layout.svelte
+++ b/src/routes/+layout.svelte
@@ -1,18 +1,17 @@
<script>
import "../app.css";
- // import Login from '$lib/login.svelte';
- import { tabs, userPublickey } from "$lib/state";
- // import {ndk} from '$lib/ndk';
- import { browser } from "$app/environment";
- import { NDKNip07Signer } from "@nostr-dev-kit/ndk";
- import { onMount } from "svelte";
import Navigation from "$lib/components/Navigation.svelte";
+ import { onMount } from "svelte";
+
+ // Compute viewport height.
+ $: displayHeight = window.innerHeight;
+
+ onMount(() => {
+ document.body.style.height = `${displayHeight}px`;
+ });
</script>
-<div class='leather h-full w-full flex flex-col items-center'>
+<div class={'leather h-full w-full flex flex-col items-center'}>
<Navigation class='sticky top-0' />
<slot />
</div>
-
-<style>
-</style>
diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte
index 243acd0..852021b 100644
--- a/src/routes/+page.svelte
+++ b/src/routes/+page.svelte
@@ -2,25 +2,23 @@
import ArticleHeader from "$lib/ArticleHeader.svelte";
import { FeedType, indexKind } from "$lib/consts";
import { ndk } from "$lib/ndk";
- import { NDKEvent, NDKRelayList, NDKRelaySet, NDKSubscriptionCacheUsage, type NDKUser } from "@nostr-dev-kit/ndk";
- import { Button, Dropdown, Radio } from "flowbite-svelte";
+ import { filterValidIndexEvents } from "$lib/utils";
+ import { NDKEvent, NDKRelayList, NDKRelaySet, type NDKUser } from "@nostr-dev-kit/ndk";
+ import { Button, Dropdown, Radio, Skeleton } from "flowbite-svelte";
import { ChevronDownOutline } from "flowbite-svelte-icons";
const getEvents = (): Promise<Set<NDKEvent>> =>
- $ndk.fetchEvents(
- // @ts-ignore
- { kinds: [indexKind] },
- );
+ // @ts-ignore
+ $ndk.fetchEvents({ kinds: [indexKind] }).then(filterValidIndexEvents);
const getEventsFromUserRelays = (userRelays: NDKRelayList): Promise<Set<NDKEvent>> => {
const relaySet = NDKRelaySet.fromRelayUrls(userRelays!.readRelayUrls, $ndk);
- // TODO: Add more filter parameters to customize the event feed.
return $ndk.fetchEvents(
// @ts-ignore
{ kinds: [indexKind] },
relaySet,
- );
+ ).then(filterValidIndexEvents);
};
const getEventsFromUserFollows = (follows: Set<NDKUser>, userRelays: NDKRelayList): Promise<Set<NDKEvent>> => {
@@ -34,7 +32,7 @@
kinds: [indexKind]
},
relaySet,
- );
+ ).then(filterValidIndexEvents);
};
const getFeedTypeFriendlyName = (feedType: FeedType): string => {
@@ -48,6 +46,19 @@
}
};
+ const getSkeletonIds = (): string[] => {
+ const skeletonHeight = 124; // The height of the skeleton component in pixels.
+
+ // Determine the number of skeletons to display based on the height of the screen.
+ const skeletonCount = Math.floor(window.innerHeight / skeletonHeight) - 2;
+
+ const skeletonIds = [];
+ for (let i = 0; i < skeletonCount; i++) {
+ skeletonIds.push(`skeleton-${i}`);
+ }
+ return skeletonIds;
+ }
+
let user: NDKUser | null | undefined;
let readRelays: NDKRelayList | null | undefined;
let userFollows: Set<NDKUser> | null | undefined;
@@ -64,11 +75,17 @@
{#key user}
{#if user == null || readRelays == null}
{#await getEvents()}
- <p>Loading...</p>
- {:then events}
- {#each Array.from(events) as event}
- <ArticleHeader {event} />
+ {#each getSkeletonIds() as id}
+ <Skeleton size='lg' id={id} />
{/each}
+ {:then events}
+ {#if events.size > 0}
+ {#each Array.from(events) as event}
+ <ArticleHeader {event} />
+ {/each}
+ {:else}
+ <p class='text-center'>No articles found.</p>
+ {/if}
{/await}
{:else}
<div class='leather w-full flex justify-end'>
@@ -86,19 +103,31 @@
</div>
{#if feedType === FeedType.Relays && readRelays != null}
{#await getEventsFromUserRelays(readRelays)}
- <p>Loading...</p>
- {:then events}
- {#each Array.from(events) as event}
- <ArticleHeader {event} />
+ {#each getSkeletonIds() as id}
+ <Skeleton size='lg' id={id} />
{/each}
+ {:then events}
+ {#if events.size > 0}
+ {#each Array.from(events) as event}
+ <ArticleHeader {event} />
+ {/each}
+ {:else}
+ <p class='text-center'>No articles found.</p>
+ {/if}
{/await}
{:else if feedType === FeedType.Follows && userFollows != null}
{#await getEventsFromUserFollows(userFollows, readRelays)}
- <p>Loading...</p>
- {:then events}
- {#each Array.from(events) as event}
- <ArticleHeader {event} />
+ {#each getSkeletonIds() as id}
+ <Skeleton size='lg' id={id} />
{/each}
+ {:then events}
+ {#if events.size > 0}
+ {#each Array.from(events) as event}
+ <ArticleHeader {event} />
+ {/each}
+ {:else}
+ <p class='text-center'>No articles found.</p>
+ {/if}
{/await}
{/if}
{/if}