Skip to content

feat: add Educational Resource (kind 30142) with AMB metadata support#260

Open
sroertgen wants to merge 1 commit intopurrgrammer:mainfrom
edufeed-org:main
Open

feat: add Educational Resource (kind 30142) with AMB metadata support#260
sroertgen wants to merge 1 commit intopurrgrammer:mainfrom
edufeed-org:main

Conversation

@sroertgen
Copy link

Based on NIP-AMB I implemented support for rendering kind 30142 events.

  • Feed renderer (EducationalResourceRenderer.tsx) — compact card with thumbnail (broken image fallback to BookOpen icon), title, primary resource URL (bookmark-style
    with ExternalLink icon), type/language/level badges, description, keywords, and creator attribution
  • Detail renderer (EducationalResourceDetailRenderer.tsx) — full metadata view with prominent resource URL in header, image via MediaEmbed, description, metadata grid
    (language, level, resource type, audience, license, dates), creators, subjects, keywords, references, and related resources
  • AMB helpers (amb-helpers.ts) — cached helper functions using getOrComputeCachedValue for extracting all AMB metadata fields from event tags
  • Kind registry (kinds.ts) — kind 30142 registered with GraduationCap icon
  • NIP badge (NIPBadge.tsx + CommunityNIPDetailRenderer.tsx) — NIP-AMB links to community NIP event (kind 30817)
  • Tests (amb-helpers.test.ts) — 26 tests covering all helper functions

Reference: https://jumble.social/notes/nevent1qvzqqqqqqypzq8zl7092ekzzcqwu4rehsgcmzesh29kjznd8t3aw4wlfu8h7ns8kqyt8wumn8ghj7un9d3shjtnddaehgu3wwp6kytcprdmhxue69uhhg6r9vehhyetnwshxummnw3erztnrdakj7qpq5nc2ycgst5qaccus3nq5pt3hzrldzrazjaclaj5ghyq6zz65yqxst777h9

🤙

- Add feed and detail renderers for AMB Educational Resource events
- Add amb-helpers library with cached helper functions and tests
- Handle broken thumbnail images with BookOpen placeholder fallback
- Surface primary resource URL prominently in both renderers (bookmark-style)
- Register kind 30142 with GraduationCap icon in kind registry
- Link NIP-AMB badge to community NIP event (kind 30817)
@vercel
Copy link

vercel bot commented Mar 5, 2026

@sroertgen is attempting to deploy a commit to the bandarra-protonmailc's Team Team on Vercel.

A member of the Team first needs to authorize it.

@vercel
Copy link

vercel bot commented Mar 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
grimoire Ready Ready Preview, Comment Mar 8, 2026 10:01am

Request Review

Copy link
Owner

@purrgrammer purrgrammer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great job, looks solid overall. some minor nits and we can go ahead and merge it.

import { UserName } from "@/components/nostr/UserName";
import { MediaEmbed } from "../MediaEmbed";

interface Kind30142DetailRendererProps {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can probably use a more human friendly name for these components, I started with kindxxxrenderer convention but moved away from it

<div className="grid grid-cols-2 gap-3 py-2 text-sm">
{language && (
<MetadataField label="Language">
<span className="font-mono">{language}</span>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could localize language names with Intl.DisplayName if these are language coded

{licenseId && (
<MetadataField label="License">
{licenseId.startsWith("http") ? (
<a
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we have a component for links but I might be wrong.

</MetadataField>
)}

{dateCreated && (
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

localizing dated would be nice

<Section title="Keywords">
<div className="flex flex-wrap gap-1.5">
{keywords.map((kw) => (
<Label key={kw}>#{kw}</Label>
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can leave out the # symbol

<BaseEventContainer event={event}>
<div className="flex gap-3">
{/* Thumbnail */}
{image && !imgError && (
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather not have images in feed renderers except for very specific kinds like emoji and badged

{keywords.length > 0 && (
<div className="flex flex-wrap gap-1">
{keywords.map((kw) => (
<Label key={kw} className="text-primary/80">
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above regarding #

const communityNip = getCommunityNipForNipId(nipNumber);

const openNIP = () => {
if (communityNip) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice, reminder to self to review community kinds and add a pointer to them

// ============================================================================

/** Get base language code from browser (e.g., "de", "en") */
function getBrowserLanguage(): string {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have some utilities for locale detection, maybe can reuse those

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants