digest email: replace reply/reaction counts with relay URL
All checks were successful
CI / checks (pull_request) Successful in 39s

- Replace `{{Replies}}`/`{{Reactions}}` stat items in digest.mjml with
  a single `{{RelayUrl}}` item showing the posting relay domain
- src/digest.ts: add `RelayUrl` to template variables, derive from
  existing `relayByEventId` map; strip unused `spec`, `getParentId`,
  `repliesByParentId`, and `context` destructure
- test/digest-reply-stats.test.ts: update assertion to check
  `parentEntry.RelayUrl` instead of `Replies`/`Reactions`
- script/render-preview.mjs: update sample data to use `RelayUrl`
This commit is contained in:
Agent 2026-09-22 10:04:05 -04:00
parent dbe4f6741d
commit b7592044c0
4 changed files with 12 additions and 22 deletions

View file

@ -24,8 +24,7 @@ const sample = {
Icon: 'https://i.pravatar.cc/150?img=32',
Name: 'carol',
Content: '<p>Does anyone know how relay-based groups work?</p>',
Replies: 5,
Reactions: 8,
RelayUrl: 'relay.damus.io',
},
{
Link: 'https://app.flotilla.social/spaces/nos.lol/community?at=1700000000',
@ -34,8 +33,7 @@ const sample = {
Name: 'bob',
Content:
'<p>Excited to share our new community space on Flotilla! <a href="https://flotilla.social">Check it out</a>.</p>',
Replies: 14,
Reactions: 32,
RelayUrl: 'nos.lol',
},
],
unsubscribeUrl: 'https://app.flotilla.social/unsubscribe?token=abc123',

View file

@ -1,6 +1,5 @@
import { neventEncode, decode } from 'nostr-tools/nip19'
import {
spec,
sortBy,
groupBy,
displayList,
@ -10,7 +9,6 @@ import { parse, truncate, renderAsHtml } from '@welshman/content'
import {
TrustedEvent,
normalizeRelayUrl,
getParentId,
NOTE,
COMMENT,
REACTION,
@ -50,23 +48,23 @@ export class Digest {
const getEventVariables = (event: TrustedEvent) => {
const parsed = truncate(parse(event), { minLength: 400, maxLength: 800, mediaLength: 50 })
const relayUrl = data.relayByEventId.get(event.id)
return {
Link: buildLink(event, handler, data.relayByEventId.get(event.id)),
Link: buildLink(event, handler, relayUrl),
Timestamp: formatter.format(secondsToDate(event.created_at)),
Icon: profilesByPubkey.get().get(event.pubkey)?.picture,
Name: displayProfileByPubkey(event.pubkey),
Content: renderAsHtml(parsed, { createElement, renderEntity }).toString(),
Replies:
repliesByParentId.get(event.id)?.filter((e) => [COMMENT, NOTE].includes(e.kind))
?.length || 0,
Reactions: repliesByParentId.get(event.id)?.filter(spec({ kind: REACTION }))?.length || 0,
RelayUrl: relayUrl
? normalizeRelayUrl(relayUrl).replace(/^wss:\/\//, '').replace(/\/$/, '')
: '',
}
}
const { events, context } = data
const { events } = data
const formatter = getFormatter()
const handler = await this.loadHandler()
const repliesByParentId = groupBy(getParentId, context)
const eventsByPubkey = groupBy((e) => e.pubkey, events)
const userProfile = profilesByPubkey.get().get(this.sub.pubkey)
const sorted = sortBy((e) => e.created_at, events).slice(0, 100)

View file

@ -64,10 +64,7 @@
<div class="event-content">{{{Content}}}</div>
<div class="event-stats">
<span class="stat-item">
{{Replies}} replies
</span>
<span class="stat-item">
{{Reactions}} reactions
Posted to {{RelayUrl}}
</span>
</div>
</div>

View file

@ -1,6 +1,5 @@
import { describe, it, expect, vi, beforeEach } from 'vitest'
import type { TrustedEvent } from '@welshman/util'
import { spec } from '@welshman/lib'
// ── Shared state accessible from both vi.mock factories and test body ──────
const mockRepo = vi.hoisted(() => {
@ -159,9 +158,7 @@ describe('digest reply/reaction stats', () => {
expect(latest.length).toBeGreaterThanOrEqual(1)
const parentEntry = latest[0]
// When context includes reply/reaction events loaded from the repository,
// Replies should be >= 1 and Reactions >= 1
expect(parentEntry.Replies).toBeGreaterThanOrEqual(1)
expect(parentEntry.Reactions).toBeGreaterThanOrEqual(1)
// RelayUrl should be the relay domain stripped of protocol and trailing slash
expect(parentEntry.RelayUrl).toBe('relay.example.com')
})
})