Browse Source

Filter for valid NKBIP-01 events

master
buttercat1791 2 years ago committed by limina1
parent
commit
a67ac58d5e
  1. 2
      package.json
  2. 25
      src/lib/utils.ts
  3. 19
      src/routes/+layout.svelte
  4. 71
      src/routes/+page.svelte

2
package.json

@ -1,6 +1,6 @@
{ {
"name": "alexandria", "name": "alexandria",
"version": "0.0.3", "version": "0.0.4",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

25
src/lib/utils.ts

@ -1,5 +1,6 @@
import type { NDKEvent } from "@nostr-dev-kit/ndk"; import type { NDKEvent } from "@nostr-dev-kit/ndk";
import { nip19 } from "nostr-tools"; import { nip19 } from "nostr-tools";
export function neventEncode(event: NDKEvent, relays: string[]) { export function neventEncode(event: NDKEvent, relays: string[]) {
return nip19.neventEncode({ return nip19.neventEncode({
id: event.id, id: event.id,
@ -81,3 +82,27 @@ export function isElementInViewport(el: string | HTMLElement) {
rect.right <= (window.innerWidth || document.documentElement.clientWidth) 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;
}

19
src/routes/+layout.svelte

@ -1,18 +1,17 @@
<script> <script>
import "../app.css"; 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 Navigation from "$lib/components/Navigation.svelte";
import { onMount } from "svelte";
// Compute viewport height.
$: displayHeight = window.innerHeight;
onMount(() => {
document.body.style.height = `${displayHeight}px`;
});
</script> </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' /> <Navigation class='sticky top-0' />
<slot /> <slot />
</div> </div>
<style>
</style>

71
src/routes/+page.svelte

@ -2,25 +2,23 @@
import ArticleHeader from "$lib/ArticleHeader.svelte"; import ArticleHeader from "$lib/ArticleHeader.svelte";
import { FeedType, indexKind } from "$lib/consts"; import { FeedType, indexKind } from "$lib/consts";
import { ndk } from "$lib/ndk"; import { ndk } from "$lib/ndk";
import { NDKEvent, NDKRelayList, NDKRelaySet, NDKSubscriptionCacheUsage, type NDKUser } from "@nostr-dev-kit/ndk"; import { filterValidIndexEvents } from "$lib/utils";
import { Button, Dropdown, Radio } from "flowbite-svelte"; 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"; import { ChevronDownOutline } from "flowbite-svelte-icons";
const getEvents = (): Promise<Set<NDKEvent>> => const getEvents = (): Promise<Set<NDKEvent>> =>
$ndk.fetchEvents( // @ts-ignore
// @ts-ignore $ndk.fetchEvents({ kinds: [indexKind] }).then(filterValidIndexEvents);
{ kinds: [indexKind] },
);
const getEventsFromUserRelays = (userRelays: NDKRelayList): Promise<Set<NDKEvent>> => { const getEventsFromUserRelays = (userRelays: NDKRelayList): Promise<Set<NDKEvent>> => {
const relaySet = NDKRelaySet.fromRelayUrls(userRelays!.readRelayUrls, $ndk); const relaySet = NDKRelaySet.fromRelayUrls(userRelays!.readRelayUrls, $ndk);
// TODO: Add more filter parameters to customize the event feed.
return $ndk.fetchEvents( return $ndk.fetchEvents(
// @ts-ignore // @ts-ignore
{ kinds: [indexKind] }, { kinds: [indexKind] },
relaySet, relaySet,
); ).then(filterValidIndexEvents);
}; };
const getEventsFromUserFollows = (follows: Set<NDKUser>, userRelays: NDKRelayList): Promise<Set<NDKEvent>> => { const getEventsFromUserFollows = (follows: Set<NDKUser>, userRelays: NDKRelayList): Promise<Set<NDKEvent>> => {
@ -34,7 +32,7 @@
kinds: [indexKind] kinds: [indexKind]
}, },
relaySet, relaySet,
); ).then(filterValidIndexEvents);
}; };
const getFeedTypeFriendlyName = (feedType: FeedType): string => { 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 user: NDKUser | null | undefined;
let readRelays: NDKRelayList | null | undefined; let readRelays: NDKRelayList | null | undefined;
let userFollows: Set<NDKUser> | null | undefined; let userFollows: Set<NDKUser> | null | undefined;
@ -64,11 +75,17 @@
{#key user} {#key user}
{#if user == null || readRelays == null} {#if user == null || readRelays == null}
{#await getEvents()} {#await getEvents()}
<p>Loading...</p> {#each getSkeletonIds() as id}
{:then events} <Skeleton size='lg' id={id} />
{#each Array.from(events) as event}
<ArticleHeader {event} />
{/each} {/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} {/await}
{:else} {:else}
<div class='leather w-full flex justify-end'> <div class='leather w-full flex justify-end'>
@ -86,19 +103,31 @@
</div> </div>
{#if feedType === FeedType.Relays && readRelays != null} {#if feedType === FeedType.Relays && readRelays != null}
{#await getEventsFromUserRelays(readRelays)} {#await getEventsFromUserRelays(readRelays)}
<p>Loading...</p> {#each getSkeletonIds() as id}
{:then events} <Skeleton size='lg' id={id} />
{#each Array.from(events) as event}
<ArticleHeader {event} />
{/each} {/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} {/await}
{:else if feedType === FeedType.Follows && userFollows != null} {:else if feedType === FeedType.Follows && userFollows != null}
{#await getEventsFromUserFollows(userFollows, readRelays)} {#await getEventsFromUserFollows(userFollows, readRelays)}
<p>Loading...</p> {#each getSkeletonIds() as id}
{:then events} <Skeleton size='lg' id={id} />
{#each Array.from(events) as event}
<ArticleHeader {event} />
{/each} {/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} {/await}
{/if} {/if}
{/if} {/if}

Loading…
Cancel
Save