Skip to content
Development
Skill

/feed-patterns

Feed composition and data-access layer patterns in Amethyst. Use when adding or modifying a feed (home, profile, hashtag, bookmarks, notifications, DMs, communities), working with the shared `FeedFilter` / `AdditiveFeedFilter` / `ChangesFlowFilter` / `FeedContentState` in

From plugin
amethyst
1.6k30 skills3 commands
Install
$ npx -y skills add vitorpamplona/amethyst --skill feed-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/feed-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

Feed composition and data-access layer patterns in Amethyst. Use when adding or modifying a feed (home, profile, hashtag, bookmarks, notifications, DMs, communities), working with the shared `FeedFilter` / `AdditiveFeedFilter` / `ChangesFlowFilter` / `FeedContentState` in

SKILL.md

feed-patterns.SKILL.md
name: feed-patterns
description: Feed composition and data-access layer patterns in Amethyst. Use when adding or modifying a feed (home, profile, hashtag, bookmarks, notifications, DMs, communities), working with the shared `FeedFilter` / `AdditiveFeedFilter` / `ChangesFlowFilter` / `FeedContentState` in `commons/.../ui/feeds/`, the Android-only `AdditiveComplexFeedFilter` / `FilterByListParams` in `amethyst/.../ui/dal/`, or extending the `FeedViewModel` family in `commons/.../viewmodels/`. Covers how feeds scan `LocalCache`, react to changes, apply ordering, and render through Compose.

Feed Patterns

Amethyst's "feed" abstraction is: a `FeedFilter` that decides which notes belong in a list, plus a `FeedViewModel` that exposes the current state reactively to the UI. Every scrollable list — home, profile, hashtag, bookmarks, notifications, DMs — is a variant of this.

When to Use This Skill

  • Adding a new screen that shows a list of notes.
  • Modifying an existing feed's filtering / ordering / inclusion rules.
  • Investigating why a feed doesn't update after a mute/follow/bookmark change.
  • Deciding whether to extend a ViewModel or write a new filter.
  • Understanding the Android ⇄ Desktop sharing boundary for feeds.

Architecture

┌─────────────────────────────────────────────────────────────┐
│ commons/.../viewmodels/  (shared, KMP)                      │
│   FeedViewModel  ◄── ListChangeFeedViewModel                │
│                 ◄── ChatroomFeedViewModel                   │
│                 ◄── MarmotGroupFeedViewModel                │
│                                                             │
│                                                             │
│ commons/.../ui/feeds/  (shared, KMP)                        │
│   IFeedFilter / FeedFilter<T>  (abstract base)              │
│   IAdditiveFeedFilter / AdditiveFeedFilter<T>               │
│   ChangesFlowFilter                                         │
│   FeedContentState, FeedState — the flow the UI collects    │
└─────────────────────────────────────────────────────────────┘
              ▲
              │ uses
              │
┌─────────────────────────────────────────────────────────────┐
│ amethyst/.../ui/dal/  (Android-only additions)              │
│   AdditiveComplexFeedFilter<T, U>                           │
│   FilterByListParams                                        │
│   DefaultFeedOrder (Note/Event/Card comparators)            │
│   (FeedFilters.kt & ChangesFlowFilter.kt here are just      │
│    back-compat typealiases re-exporting commons)            │
│                                                             │
│   Concrete feeds: HomeNewThreadFeedFilter,                  │
│   HashtagFeedFilter, NotificationFeedFilter, … live in      │
│   feature folders under ui/screen/loggedIn/*/dal/           │
└─────────────────────────────────────────────────────────────┘
              ▲
              │ reads
              │
┌─────────────────────────────────────────────────────────────┐
│ model/LocalCache.kt + account.<feature>.flow                │
└─────────────────────────────────────────────────────────────┘

Key Files

Shared (commons)

`commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/viewmodels/`:

  • **`FeedViewModel.kt`** — `abstract class FeedViewModel(localFilter, cacheProvider)`. Holds a `FeedContentState`, subscribes to invalidation signals (from `Account` flows and `LocalCacheFlow`), re-runs the filter, and emits a new `FeedState` for the UI.
  • **`ListChangeFeedViewModel.kt`** — specialization for feeds whose membership changes frequently (e.g. bookmarks).
  • **`ChatroomFeedViewModel.kt`** — DM thread feed.
  • **`MarmotGroupFeedViewModel.kt`** — NIP-29 / marmot group feed.
  • **`LiveStreamTopZappersViewModel.kt`, `SearchBarState.kt`, `ChatNewMessageState.kt`** — narrower, non-feed states that share the plumbing.

Shared filter bases (commons)

`commons/src/commonMain/kotlin/com/vitorpamplona/amethyst/commons/ui/feeds/`:

  • **`FeedFilter.kt`** — `abstract class FeedFilter<T> : IFeedFilter<T>`. Has `feed(): List<T>` (the sync query against the cache), `feedKey(): String` (identity used to cache), `limit()`, and `loadTop()`.
  • **`AdditiveFeedFilter.kt`** — `abstract class AdditiveFeedFilter<T> : FeedFilter<T>(), IAdditiveFeedFilter<T>`. Adds incremental updates (the "additive" part): `updateListWith(oldList, newItems)` runs `applyFilter(newItems)` and grafts accepted items onto the existing list (re-`sort` + `take(limit())`) without recomputing everything.
  • **`ChangesFlowFilter.kt`** — wraps a filter with a coarse "state changed" signal so the ViewModel knows to re-query.
  • **`FeedContentState.kt` / `FeedState.kt`** — the reactive state the UI collects.

Android DAL (additions on top)

`amethyst/src/main/java/com/vitorpamplona/amethyst/ui/dal/`:

  • **`AdditiveComplexFeedFilter.kt`** — `abstract class AdditiveComplexFeedFilter<T, U> : FeedFilter<T>()`: like `AdditiveFeedFilter` but the incoming items (`Set<U>`) are a different type than the list rows (`T`).
  • **`FilterByListParams.kt`** — common parameters (top-nav filter, exclude muted, since/until) shared across many filters.
  • **`DefaultFeedOrder.kt`** — standard comparators (`createdAt` desc + id tiebreaker for stable paging) for `Note`, `Event`, and `Card`.
  • **`FeedFilters.kt` / `ChangesFlowFilter.kt`** — back-compat typealiases re-exporting the commons classes; don't add logic here.

Concrete filters (Home, Hashtag, Profile, Bookmark, Notifications, Communities, etc.) live in feature `dal/` subfolders under `amethyst/.../ui/screen/loggedIn/*/` — each extends `FeedFilter`, `AdditiveFeedFilter`, or `AdditiveComplexFeedFilter`. Desktop has its own in `desktopApp/.../feeds/DesktopFeedFilters.kt`.

Adding a New Feed

1. **Define the filter.** Extend `AdditiveFeedFilter<Note>` (or plain `FeedFilter<Note>` if additivity doesn't matter; `AdditiveComplexFeedFilter<T, U>` if incoming items differ

Read more
Ships withamethyst

Nostr client for Android

Get the whole plugin

Other skills on amethyst.