Files
amethyst/desktopApp/plans/_desktop-feature-backlog.md
T
nrobi144 7a18e30fc4 feat(desktop): hashtag-spam filter with collapse-with-reveal
Damus-inspired content filter that collapses notes abusing `t` hashtag
tags into a compact reveal-on-click placeholder. Ships default ON with a
threshold of 5 (adjustable 1–20 in Settings → Content Filters, or off).

Scope
- Pure check (`HashtagSpamCheck`) + settings interface
  (`HashtagSpamSettings`) live in `commons/moderation/`, callable by
  Desktop, `amy` CLI, and (future) Android.
- JVM-backed `PreferencesHashtagSpamSettings` writes to the shared
  `java.util.prefs` node `com/vitorpamplona/amethyst/filters`, so `amy`
  and Desktop observe the same value automatically.
- `CollapsedSpamNote` placeholder in `commons/ui/note/` takes only
  primitive scalars so Android can adopt it without touching commons.
- Desktop wraps every `NoteCard` call site (FeedNoteCard, QuotedNoteEmbed,
  BookmarksScreen, 5 SearchResultsList sites) with a shared
  `SpamCheckedNoteRender` helper. Thread root notes auto-expand via
  `forceReveal=true`; replies still respect the filter.

Exemptions
- Long-form articles (kind 30023)
- Authors in the follow list plus self
- Repost wrappers check the inner event's tags via precomputed
  `note.replyTo`, falling back to `containedPost()`

Search UX fixes bundled in
- Removed the `#hashtag` → "Direct lookup" card. `QueryParser` already
  extracts `#xxx` into the query's hashtag filter, so typing `#bitcoin`
  now goes straight to filtered results.
- Search-result rows now trigger metadata loading via
  `subscriptionsCoordinator.loadMetadataBatched(authors)` and observe
  each user's metadata flow via a new `rememberDisplayData` helper, so
  display names + avatars refresh when kind-0 arrives from index
  relays. Same helper reused in Bookmarks.

Tests + docs
- 19 unit tests (check × 10, displayed-event unwrap × 4, prefs × 5),
  all green.
- Manual testing sheet with 16 scenarios at
  `desktopApp/plans/2026-06-29-hashtag-spam-filter-manual-testing-sheet.md`.
- Plan at `docs/plans/2026-06-29-feat-desktop-hashtag-spam-filter-plan.md`.
- Cross-client desktop feature backlog reference at
  `desktopApp/plans/_desktop-feature-backlog.md`.
2026-07-01 09:46:43 +03:00

143 lines
6.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Desktop Feature Backlog — Inspiration from Nostr Ecosystem
Survey date: 2026-06-29. Source: cross-client research across top-5 used clients
(Damus, Primal, YakiHonne, Snort, Iris) + top-5 desktop-first / power-user
clients (Notedeck, Gossip, Jumble, Coop, Flotilla). Wisp investigated separately
(Android-only, not desktop).
Already shipped on Amethyst Desktop and excluded from this list: deck columns,
embedded local relay, NIP-46 bunker login, NWC zapping, custom feeds, advanced
search, follow packs (in progress).
---
## Priority queue (next up)
### 1. Hashtag-spam filter — NEXT
- **Inspired by:** Damus (auto-hide posts above N hashtags).
- **What:** Configurable threshold; posts with `> N` `t` tags get hidden or
collapsed across all feeds. User-defined whitelist for legit multi-tag use
(events, longform).
- **Why desktop:** Spam noise scales with column count on a deck UI; one filter
cleans every column at once.
- **Module:** `commons/` (filter logic, shared with Android in future) +
`desktopApp/` (settings UI).
- **Skills:** `feed-patterns`, `compose-expert`, `account-state`.
### 2. WoT (web-of-trust) score on avatars + filters
- **Inspired by:** Gossip, Snort.
- **What:** Friends-of-friends score (0N) computed from follow graph. Visual
badge/ring on avatar. Threshold filter for notifications/DM-requests.
- **Why desktop:** At-a-glance trust signal scales with multi-column deck.
- **Module:** `commons/services/WoTService.kt` (StateFlow<Map<HexKey, Int>>),
avatar composable in `commons/.../note/`.
- **Skills:** `account-state`, `kotlin-flow-state-event-modeling`,
`compose-expert`. Score compute is O(follows × follows) — must be lazy /
throttled.
---
## Full ranked backlog (10 ideas)
Ranking heuristic: impact × novelty / implementation cost. Items 12 are the
priority queue above; 310 captured for later.
| # | Feature | Inspired by | Module | Notes |
|---|---------|-------------|--------|-------|
| 1 | Hashtag-spam filter | Damus | commons + desktopApp | **NEXT** |
| 2 | WoT score on avatars + filters | Gossip, Snort | commons | After #1 |
| 3 | Relay-as-column + Relay Sets first-class | Jumble, Flotilla | commons | Drag relay URL → column |
| 4 | Cashu ecash wallet alongside NWC | Iris, Primal Spark | quartz + commons | Bearer-token privacy |
| 5 | AI "Dave" column (timeline-aware assistant) | Notedeck | desktopApp | LLM reads adjacent columns |
| 6 | Algorithmic Feed Marketplace | Primal v3.0 | commons | Discover layer on custom feeds |
| 7 | Keyboard / command palette (`Ctrl+K`, `?` overlay) | Snort, Notedeck | desktopApp + commons | Power-user nav |
| 8 | Longform reader column + offline publish queue | Damus, YakiHonne | commons | NIP-23, uses local relay |
| 9 | WoT-scored notification filter + undo-send | Gossip, Snort | commons | Calmer UX |
| 10 | Per-relay column health (sparkline + dot) | Wisp, Gossip | commons | Reuse EOSE manager |
| 11 | Discord-style "communities" sidebar (NIP-29) | Flotilla, Chachi | desktopApp + commons | Sidebar second purpose |
(Extra row #11 added: communities sidebar was the 10th in the original survey;
WoT-notif-filter + undo-send promoted to its own row for clarity.)
---
## Per-item detail
### #3 Relay-as-column + Relay Sets first-class
- **Drop:** Drag relay URL onto the deck → new column showing that relay's
global. Relay Sets become saveable column templates.
- **Impl:** New `RelayUrlFeedFilter` + `RelaySetFeedFilter` in `commons/feeds/`.
- **Gotcha:** Bypass write-through to local store for these feeds, otherwise
local relay pollutes with arbitrary global content.
### #4 Cashu ecash wallet alongside NWC
- **Why:** Different privacy model (bearer tokens, no account). Power users
want both — small/private = ecash, big/recurring = NWC.
- **Impl:** Check Quartz NIP-60/61 coverage; else new `wallet/cashu/`. UI is a
second tab in existing wallet column.
- **Gotcha:** Mint trust UX; token backup/restore; encryption story
(`accounts.json.enc` slot).
### #5 AI "Dave" column
- **Why:** Big-screen-only feature that breaks the deck-column metaphor wide
open. Reads N adjacent columns as context.
- **Impl:** `desktopApp/` for API key config + `commons/ai/` for prompt builders.
Pluggable model: Ollama local default, OpenAI/Anthropic optional.
- **Gotcha:** Privacy story — must be explicit which events get sent where.
Default to local Ollama.
### #6 Algorithmic Feed Marketplace
- **Why:** Custom feeds already shipped — marketplace is the discovery layer.
- **Impl:** Feeds are published as kind:30000-ish lists or DVM-driven. Browser
UI + subscribe button on top of existing custom-feed infra.
- **Gotcha:** DVM feeds vs static list feeds — two execution paths.
### #7 Keyboard / command palette
- **Why:** Mouse-first ≠ keyboard-hostile.
- **Impl:** `desktopApp/` global `onPreviewKeyEvent` + `commons/ui/CommandPalette.kt`.
- **Gotcha:** Focus management across deck columns; cmd vs ctrl on macOS;
conflict with text-input fields.
### #8 Longform reader + offline publish queue
- **Why:** NIP-23 is barely surfaced on Desktop. Big screens are *the* surface
for reading articles.
- **Impl:** New column types `LongformReader` + `LongformComposer`. Composer is
block-based. Offline queue writes drafts to local relay; auto-broadcasts on
reconnect.
- **Gotcha:** Markdown rendering on Compose Desktop is mid — pick or build.
Blossom upload alongside drafts.
### #9 WoT notif filter + undo-send
- **Why:** Notifications scale poorly; slow-mode (delay sends 530 s with
toast-cancel) calms power-user UX.
- **Impl:** `NotificationFilter.kt` extension + send-pipeline interceptor in
`commons/relayClient/`.
- **Gotcha:** Sent-but-cancelled events already in local relay write-through —
must scrub from local store on cancel.
### #10 Per-relay column health
- **Why:** Deck UI exposes the multi-relay reality. Latency/EOSE/dup-rate dot +
sparkline in column header helps self-tune.
- **Impl:** Extend EOSE manager to emit `RelayHealth` per relay; render small
sparkline + colored dot.
- **Gotcha:** Memory over time — rolling 60 s window, decay older.
### #11 Communities sidebar (NIP-29)
- **Why:** Amethyst Desktop sidebar is nav-only; mapping joined groups /
favourite relays to Discord-like server icons doubles its purpose.
- **Impl:** `desktopApp/.../sidebar/` new section; on click, populate deck with
group channels. `commons/groups/` for NIP-29 if not in Quartz already.
- **Gotcha:** NIP-29 spec churn — check Quartz coverage. Don't conflate
"favourite relay" with "joined group".
---
## Sources
- nostr.com/clients, nostrapps.com, stats.nostr.band
- humai.blog "Best Nostr Apps 2026"; nostr.co.uk/clients; nostrcompass.org #15
- opensats.org "Advancements in Nostr Clients"
- Repos: damus-io/damus, damus-io/notedeck, mikedilger/gossip, CodyTseng/jumble,
lumehq/coop, coracle-social/flotilla (active at gitea.coracle.social),
barrydeen/wisp