From eb9aa293eeec87dd1fb0bd8d5ff268578efb3eb5 Mon Sep 17 00:00:00 2001 From: Chris Troutner Date: Wed, 26 Aug 2026 06:05:46 -0700 Subject: [PATCH] Specify Like / Tip a Memo By specifier. --- dev-docs/psf-memo-db-changes.md | 63 ++++++++++++++++++ specs/like-tip-memo.feature | 112 ++++++++++++++++++++++++++++++++ 2 files changed, 175 insertions(+) create mode 100644 dev-docs/psf-memo-db-changes.md create mode 100644 specs/like-tip-memo.feature diff --git a/dev-docs/psf-memo-db-changes.md b/dev-docs/psf-memo-db-changes.md new file mode 100644 index 0000000..136525c --- /dev/null +++ b/dev-docs/psf-memo-db-changes.md @@ -0,0 +1,63 @@ +# psf-memo-db changes to support Like counts + +**Status**: DRAFT — notes for a future session. The current Like/Tip feature +(`0x6d04`) focuses on the **UI to broadcast a like** (and an optional tip). The +read-side changes below are **not** implemented now; they are recorded here so the +like-count read path can be developed later. + +Owner: specifier. +Last updated: 2026-08-26. + +--- + +## Goal + +Expose like counts (and, later, liked-state and a likers list) so the psf-memo-client +UI can show a real like count on each post and whether the viewing user already liked +it. Today `/posts/*` responses omit likes entirely. + +## What the indexer already provides + +The Memo **indexer** (`psf-memo-indexer`) already parses `0x6d04` like/tip actions into +the DB as social references: a **liker address** → a **liked post txid** (with an +optional tip value). This feature does not require indexer changes to record likes; it +requires the **DB query/API** layer to aggregate and expose them. + +## Required psf-memo-db changes + +1. **`likeCount` on post responses.** + Add a `likeCount` field (number of distinct `0x6d04` references whose liked txid + equals the post txid) to the objects returned by: + - `/posts/recent` (feed items) + - `/post/:txid` (thread root) + - thread reply nodes (when replies are also likeable / shown with counts) + Aggregate the count in the query rather than an N+1 per-post lookup. + +2. **Liked-state for the viewing user (optional, later).** + To render a filled heart when the current wallet has already liked a post, the + read endpoints need to know the viewer. Add an optional `viewer=
` query + param (or equivalent) to the relevant post endpoints and return + `liked: true|false` per post based on whether `viewer` has a `0x6d04` reference to + that txid. Until this exists, the client can track "liked" locally/optimistically + for the current session only. + +3. **Likers list endpoint (later).** + memo.cash shows a modal listing who liked a post (its `post/likes`). Add an endpoint + e.g. `GET /post/:txid/likes` returning `[{ address, name, profilePicUrl, tip }...]` + for the addresses that liked the post, ordered by time/tip, joined with the profile + store to avoid N+1 lookups. Used by a future "likes" modal. + +4. **Join efficiency.** + Like counts must be aggregated server-side (e.g. a counter derived from the + reference index or a materialized count) and included in the same response as the + post text, author, name, and avatar — avoid N+1 per-item like lookups in feed and + thread responses. + +## Out of scope (this session) + +- The like-count **read surface** (count badge, liked-state, likers modal). +- `viewer` liked-state param. +- Likers list endpoint. + +All of the above are future work; the UI spec for this session only broadcasts the +like/tip and increments a count **optimistically**. diff --git a/specs/like-tip-memo.feature b/specs/like-tip-memo.feature new file mode 100644 index 0000000..14d0065 --- /dev/null +++ b/specs/like-tip-memo.feature @@ -0,0 +1,112 @@ +# Scenarios: Like / Tip a Memo - 1, Like / Tip a Memo - 2, Like / Tip a Memo - 3, Like / Tip a Memo - 4, Like / Tip a Memo - 5, Like / Tip a Memo - 6, Like / Tip a Memo - 7, Like / Tip a Memo - 8, Like / Tip a Memo - 9, Like / Tip a Memo - 10 +Feature: Like / Tip a Memo + + Background: + Given a wallet authenticated for the address bitcoincash:qqlrzp23w08434twmvr4fxw672whkjy0py26r63g3d + Given the wallet has a spendable balance of 100000 sats + Given a post with the txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa authored by the author address + + Scenario: Like / Tip a Memo - 1 the heart icon opens the like/tip modal + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + Then a like/tip modal opens for the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + + Scenario Outline: Like / Tip a Memo - 2 a pure like broadcasts the Memo like action + Given a post with the txid authored by the author address + When I click the heart icon on the post with txid + When I submit the like without a tip + Then the wallet broadcasts an OP_RETURN transaction with the Memo like prefix and the post txid + Then the wallet sends no tip + Then the like count on the post increases by one + Then the heart icon on the post shows as filled + + Examples: + | txid | + | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + + Scenario Outline: Like / Tip a Memo - 3 a like with a tip broadcasts and pays the author + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I enter a tip of + When I submit the like + Then the wallet broadcasts an OP_RETURN transaction with the Memo like prefix and the post txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + Then the wallet sends a tip of to the author address + Then the like count on the post increases by one + Then the heart icon on the post shows as filled + + Examples: + | tip | + | 3000 | + | 25000 | + + Scenario Outline: Like / Tip a Memo - 4 an invalid tip is rejected + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I enter a tip of + When I submit the like + Then the like/tip modal shows an error containing "valid number" + Then the wallet does not broadcast any transaction + + Examples: + | tip | + | 1.5 | + | abc | + + Scenario Outline: Like / Tip a Memo - 5 a tip below the dust limit is rejected + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I enter a tip of + When I submit the like + Then the like/tip modal shows an error containing "dust limit" + Then the wallet does not broadcast any transaction + + Examples: + | tip | + | 1 | + | 2999 | + + Scenario: Like / Tip a Memo - 6 a tip above the maximum is rejected + Given the wallet has a spendable balance of 150000000 sats + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I enter a tip of 100000001 + When I submit the like + Then the like/tip modal shows an error containing "maximum" + Then the wallet does not broadcast any transaction + + Scenario Outline: Like / Tip a Memo - 7 a tip above the spendable balance is rejected + Given the wallet has a spendable balance of sats + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I enter a tip of + When I submit the like + Then the like/tip modal shows an error containing "spendable" + Then the wallet does not broadcast any transaction + + Examples: + | balance | tip | + | 30000 | 35000 | + | 500000 | 550000 | + + Scenario Outline: Like / Tip a Memo - 8 a user without spendable balance cannot like + Given the wallet has a spendable balance of sats + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + Then the like/tip modal shows an error containing "add BCH" + Then the wallet does not broadcast any transaction + + Examples: + | balance | + | 0 | + | 2999 | + + Scenario Outline: Like / Tip a Memo - 9 a user can like their own post + Given a post with the txid authored by my address + When I click the heart icon on the post with txid + When I submit the like without a tip + Then the wallet broadcasts an OP_RETURN transaction with the Memo like prefix and the post txid + Then the like count on the post increases by one + + Examples: + | txid | + | aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa | + | bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb | + + Scenario: Like / Tip a Memo - 10 the cancel button closes the like/tip modal + When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + When I click the cancel button + Then the like/tip modal closes