Specify Like / Tip a Memo

By specifier.
This commit is contained in:
Chris Troutner
2026-08-26 06:05:46 -07:00
parent 401075cda6
commit eb9aa293ee
2 changed files with 175 additions and 0 deletions
+63
View File
@@ -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=<address>` 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**.
+112
View File
@@ -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 <txid> authored by the author address
When I click the heart icon on the post with txid <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 <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 <tip>
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 <tip> 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 <tip>
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 <tip>
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 <balance> sats
When I click the heart icon on the post with txid aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
When I enter a tip of <tip>
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 <balance> 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 <txid> authored by my address
When I click the heart icon on the post with txid <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 <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