39 Commits
Author SHA1 Message Date
Chris Troutner 9fb092e845 Implement recent profile identity join and Account column
DB: GET /profile/recent joins each profile's display name (names store)
and avatar URL (profilePics store) by address, reporting null when a
record is absent. The join runs on the requested page only and leaves the
profile order and pagination unchanged.

Client: the /profile/recent table gains a leftmost Account column showing
the display name and avatar, both linking to the profile, with a
truncated-address fallback when the name is absent and an identicon
fallback when the avatar is absent. The existing columns are preserved.

Add focused unit tests, acceptance fixtures, and regex step handlers for
both components.

By coder.
2026-09-20 12:46:12 -07:00
Chris Troutner d1372dac11 Merge Following feed into the posts page as Recent/Following tabs
The /posts/recent posts page now shows a row of two mode buttons. On
first load it selects Following when the viewer's address follows at
least one account (via GET /follow/following/:addr) and Recent
otherwise, and switching tabs resets the feed to its first page. The
/posts/following route and its navbar item are removed, so the
Following feed is reached only through the Following button. Add the
testable FeedTabsPage controller with unit coverage and the acceptance
handlers for feed-tabs.

Also drop the Feed Tabs - 4 assertion that the global Recent feed hides
a followed author's post: /posts/recent is the global feed, so followed
posts appear in both tabs. Approved by the user.

By coder.
2026-09-20 10:07:06 -07:00
Chris Troutner 722c486ef6 Refactor mute broadcast result: share result handlers, add property tests
Deduplicate the like/mute acceptance broadcast-result assertion handlers and
the follow/unfollow/mute/unmute prefix handlers behind shared helpers, and
extract the broadcast explorer transaction link into one component used by
both result components. Add property tests for the mute result component and
the profile page mute result state machine (explorer URL composition, error vs
success markup, determinism, outcome mirroring, failure state preservation,
and dismiss).

CRAP stays at or below 6, every changed source module stays below 100 mutation
sites, and mutation manifests are left intact for differential runs.

By refactorer.
2026-09-18 15:07:09 -07:00
Chris Troutner 24d7f0f82c Implement mute broadcast result
Show a mute/unmute broadcast result modal on the profile page: success
message, transaction id, and block explorer link on success; the broadcast
error on failure. The modal stays open until dismissed, dismissing only
closes it, and a failed broadcast leaves the button in its previous state.

By coder.
2026-09-18 14:57:37 -07:00
Chris Troutner 7dfc315840 Refactor notification entry display: share acceptance assertions, add property tests
Deduplicate the notification-entry acceptance step handlers behind shared
entry-assert helpers and collapse the repeated ready-state guards in the
notifications view. Add property tests for the entry view model (profile link
round trip, name fallback, view-model derivation, message mapping) and for
notifications page profile resolution, entry derivation, and actor lookup.

CRAP stays at or below 6 and every changed source module stays below 100
mutation sites; mutation manifests are left intact for differential runs.

By refactorer.
2026-09-18 12:30:53 -07:00
Chris Troutner 521d434524 Implement notification entry display
Name each Notifications entry with the actor's Memo display name and
avatar resolved client-side, link both to the actor's profile, show the
full address as plain text, and add a View Post link for like and reply
notifications that opens the referenced post thread. Fall back to the
truncated address and an identicon when a profile is missing or its
lookup fails.

By coder.
2026-09-18 12:20:52 -07:00
Chris Troutner ac4d9ea7f7 Render topics page as a table
Replace the topics page flex list with a react-bootstrap Table driven by a
testable topics-table view model: a labeled header row, aligned cells for
name, relative time, post count, and follower count, feed links on each row,
and a horizontal-scroll wrapper for narrow screens.

By coder.
2026-09-18 07:36:16 -07:00
Chris Troutner 8da0f09f46 Implement topic metadata columns
Add lastSeen and followerCount to topicSummaries across the indexer, DB,
and client. The indexer now tracks the newest post seen time, updates
follower counts idempotently on follow/unfollow, and preserves post
metadata. The DB exposes lastSeen and followerCount from GET /topics and
rebuilds both from the rooms store in the backfill. The client renders the
four-column topics page with relative-time labels.

By coder.
2026-09-18 05:46:58 -07:00
Chris Troutner e02bea5460 Implement topic recency ordering and pagination
- Indexer maintains topicSummaries and topicRecency for topic messages and
  follows, idempotently, moving each room's recency record to its newest post.
- DB adds the two index stores and serves GET /topics from them with
  limit/offset pagination, without iterating the rooms store. Adds
  util/room/backfill-topic-indexes.js to build the indexes idempotently.
- Client topics page loads 50 per page with Previous/Next.

By coder.
2026-09-17 07:24:45 -07:00
Chris Troutner fac01730c4 Review multi-push encoding: fix browser Buffer and harden coverage
Import the browser-safe buffer module in the multi-push adapter so the
CRA production bundle no longer reads the Node global Buffer, add
regression tests for the falsy-wallet and idempotent-attach paths, share
the script-encoding test helper, and de-duplicate the UTF-8 push
acceptance assertions. Refresh the language mutation manifests and the
soft acceptance-mutation manifest.

By architect.
2026-09-16 21:31:23 -07:00
Chris Troutner 2e2db86263 Implement multi-push encoding for Memo multi-field actions
Reply, topic message, add-poll-option, poll-vote, and create-poll now
broadcast each protocol field as its own OP_RETURN script push instead of
combining them into one push. The indexer requires three pushes for reply,
topic message, and poll children, and accepts four for create-poll; the
combined form was rejected or misparsed.

hex.js builds the txid and text as separate pushes, and a new
memo-multipush adapter teaches minimal-slp-wallet's single-push sendOpReturn
to expand an array of fields into separate pushes. Acceptance step handlers
assert the individual pushes, and unit/property tests cover the new shape.

By coder.
2026-09-16 20:37:59 -07:00
Chris Troutner dedb7a1343 Fix txid wire encoding and add txid repair utility
Client likes, replies, poll options, and poll votes now embed the
referenced txid in little-endian wire order, matching the indexer's
byte reversal. Add a psf-memo-db repair library and CLI that rewrites
byte-reversed references in likes, postParents, pollOptions, and
pollVotes and rebuilds the postLikes and postChildren indexes, leaving
correct and unknown references untouched and staying idempotent.

By coder.
2026-09-16 14:40:13 -07:00
Chris Troutner e978516a0f Refactor like result modal and share the block explorer link
Deduplicate the new like-result acceptance steps behind one render helper,
and extract the bch.loping.net transaction link shared by the New Post result
modal, the post options menu, and the like/tip result into a block-explorer
service. Cover the LikeTipPage open/parse/handler failure branches and add
property tests for the explorer URL, the LikeResult markup, and the result
modal state machine.

CRAP is at or below 5 with full coverage on the changed services; every
changed/new plain source file scans at 21 or fewer mutation sites. The
existing mutation manifests are left for the mutation tool to refresh.

By refactorer.
2026-09-16 12:03:41 -07:00
Chris Troutner a0040f98c1 Implement like broadcast result modal
Keep the like/tip modal open after a successful like and show a broadcast
result instead of closing: the success message, the like transaction id, and
a link to that transaction on bch.loping.net that opens in a new tab. The
like count and filled heart update immediately, and dismissing the result
closes the modal. The result state lives on the testable LikeTipPage
controller; a server-renderable LikeResult component backs the modal and the
acceptance adapter, with acceptance step handlers for the new wording.

By coder.
2026-09-16 11:41:49 -07:00
Chris Troutner d0882bbd82 Refactor post options menu and add property tests
Move the keyboard shortcut mapping into the pure post-options service so the
React component is a thin adapter and the Escape/ArrowDown decisions are unit
and property testable. DRY the duplicated post-options acceptance steps behind
two helpers, and add property tests for the explorer URL, menu-item shape,
state transitions, key command, and rendered markup.

The createElement post-options component and service stay scannable by
mutate4javascript; the JSX call sites (post-feed-item, profile) are unchanged
except for rendering the shared menu.

By refactorer.
2026-09-16 09:48:36 -07:00
Chris Troutner 20e67da216 Implement post options menu on every post card
Add a shared PostOptionsMenu component, used by the feed/thread post card and
the profile post card, with a pure post-options service for the block explorer
link and the open/close/focus state. Its first item links to the post
transaction on bch.loping.net in a new tab; the menu opens on the button and
closes on the button again, an outside click, or Escape, with ArrowDown moving
focus to the first item.

By coder.
2026-09-16 09:42:15 -07:00
Chris Troutner e77e174c92 Refactor post image rendering and cover image helpers
Extract the pure failed-image set transition into a testable service,
DRY the acceptance no-link/no-image assertions, and add property tests
for image URL detection, alt text, and image rendering.

By refactorer.
2026-09-16 07:20:40 -07:00
Chris Troutner 3971e0736f Render image URLs inline in posts
Recognize common image file extensions in post URLs (query string and
fragment ignored), render them as inline images inside new-tab anchors
with the filename as alt text, and fall back to a plain link when the
image fails to load. Non-image URLs keep the existing plain-link
behavior.

By coder.
2026-09-16 07:07:40 -07:00
Chris Troutner 6c7952f469 Format post URLs and bare domains as links
Render http(s) URLs and bare domains in post text as anchors that open in a
new tab. Explicit URLs keep their scheme; bare domains get an https href while
their visible text stays as written. Embeddable YouTube links keep their
embedded-player behavior.

By coder.
2026-09-15 19:08:49 -07:00
Chris Troutner 5bbe276c58 Generate acceptance entry points incrementally
Every acceptance run re-parsed and regenerated all features even when only
one changed. Record feature_hash and aps_commit in the per-feature metadata
and skip parse/generate when both match, and drop generated tests for
features that no longer exist.

By specifier.
2026-09-15 18:13:11 -07:00
Chris Troutner 66a791bd72 Single-source the APS checkout under tmp/aps
There were up to four APS copies (tmp/aps, tmp/aps-spec, tmp/aps-fresh, and a
component-local client copy) and the briefing, wrappers, and runners disagreed
on which to use. Add ensure-aps.sh (ensure/update/reclone) as the one canonical
tmp/aps checkout, point the gherkin-parser wrapper, architect startup, and all
three acceptance runners at it, and update the architect cheat-sheet.

By specifier.
2026-09-15 17:47:33 -07:00
Chris Troutner 8244e11ec4 New posts display TXID after broadcast 2026-09-13 06:42:14 -07:00
Chris Troutner afd4585374 Implement account avatar image display on the account page
By coder.
2026-09-05 13:04:24 -07:00
Chris Troutner 448bddc31f Implement mute feed filtering across recent, topic, search, and notifications feeds
By coder.
2026-09-04 12:23:00 -07:00
Chris Troutner 5e1f4739e3 Broadcast follow/mute/unfollow/unmute hash160 payloads as Uint8Array
Replaces the Node-only Buffer global in memo-follow and memo-mute with
the existing hexToBytes helper, which returns a Uint8Array. Updates unit
and property tests to assert bytes without requiring Buffer in the
production source, and adds an acceptance handler for the new binary
payload wording so the Binary Payload Broadcast regression spec passes.

By coder.
2026-09-04 11:13:15 -07:00
Chris Troutner 59cce4426d Reduce paginated client page size to 50
By coder.
2026-09-04 10:00:19 -07:00
Chris Troutner 8e606aecb1 Implement YouTube embed rendering in recent posts feed
By coder.
2026-09-04 08:50:28 -07:00
Chris Troutner 8ee07fc35f Implement Notifications read-only feature
Add psf-memo-db notifications query, use case, route, and tests.
Add psf-memo-client NotificationsPage service, React page, nav link,
route, and tests. Wire acceptance handlers for notifications.

By coder.
2026-09-02 12:53:02 -07:00
Chris Troutner 6d1bd148e9 Implement Following feed
Add psf-memo-db endpoint GET /posts/following/:addr that returns top-level
posts from followed profiles, excluding replies and the viewer's own posts,
newest first and paginated. Add PostQuery.scanFollowingFeedTxidsAndCount,
ListFollowingFeed use case, and REST controller/router wiring.

Add psf-memo-client Following feed page, route /posts/following, nav link,
MemoDb.getFollowingFeed, and FollowingFeedPage service. Update acceptance
handlers for the new following-feed.feature.

By coder.
2026-08-29 10:30:54 -07:00
Chris Troutner 8508fdaff0 Implement Search feature (P6.4)
- Add psf-memo-db /search endpoint with SearchQuery adapter, SearchAll use case,
  and REST controller/router.
- Add psf-memo-client SearchPage service, Search component, route, and nav link.
- Wire search support into the acceptance handler fake MemoDb.
- Add unit tests for the new adapter, use case, controller, and page service.

By coder.
2026-08-29 07:18:39 -07:00
Chris Troutner 4b03210e85 Implement mute/unmute user feature (0x6d16/0x6d17)
- Client: MemoMute service, ProfilePage mute controls, Profile UI Mute/Unmute buttons,
  Profiles mute state, MemoDb mute read APIs, unit/acceptance tests.
- DB: mutes LevelDB store, MuteQuery adapter, /mute REST routes, MuteState and ListMuted use cases, tests.
- Indexer: mute/unmute action codes, handleMute action handler, muteDb adapter, tests.
- Backlog: mark P4.1/P4.2 mute/unmute as shipped.

By coder.
2026-08-28 19:38:42 -07:00
Chris Troutner cf05de6630 Implement poll actions (create, option, vote) across client, indexer, and DB
- Client: add MemoPollCreate, MemoPollOption, MemoPollVote services and page controllers.
- Indexer: add create-poll, add-poll-option, and poll-vote handlers with new poll DB adapters.
- DB: add polls/pollOptions/pollVotes stores, PollQuery adapter, GET /polls/:txid endpoints, and use cases.
- Add Gherkin acceptance handlers and focused unit tests for all three components.

By coder.
2026-08-28 14:50:05 -07:00
Chris Troutner d6f56780be Refactor topic actions: reduce CRAP/DRY, add property coverage, complete acceptance
- Add unit coverage for Profiles topic follow state to bring CRAP to 4.
- DRY Profiles set/get follow state via shared nested-map helpers.
- Add TopicQuery topic-follow read-side property tests and MemoTopicPost
  byte-accounting property tests.
- Complete client and DB acceptance step handlers and mocks for the
  topic-follow and topic-post features.

By refactorer.
2026-08-28 11:13:03 -07:00
Chris Troutner 652c70e4ea Implement topic discovery and topic feed
- Add psf-memo-db /topics and /topics/:room/posts read endpoints
- Add TopicQuery adapter, ListTopics and ListTopicPosts use cases
- Add topic read acceptance fixture and step handlers
- Add psf-memo-client topic services, UI routes, nav link
- Add generated topic discovery/feed acceptance handlers

By coder.
2026-08-28 06:31:43 -07:00
Chris Troutner 247d4a9fcf Implement follow-user and follow-read behavior
- Add MemoFollow service for follow/unfollow broadcast with hash160 payloads
- Update Profiles store with follow state
- Extend ProfilePage and Profile UI with Follow/Unfollow buttons
- Add psf-memo-db /follow/state, /follow/following, /follow/followers endpoints
- Add FollowQuery adapter, use cases, REST controller, and unit tests
- Update acceptance handlers for both components

By coder.
2026-08-27 10:35:33 -07:00
Chris Troutner 7bee85a36f Implement Set Avatar URL client feature
- Add MemoSetAvatarUrl service broadcasting 0x6d0a with 217-byte limit.
- Add SetAvatarUrlPage controller and React view at /memo/set-avatar-url.
- Wire route in AppBody and add Account page avatar URL display.
- Add Set Avatar URL button on Account page linking to the editor.
- Update Profiles store, AccountPage behavior, and acceptance handlers.
- Add unit tests for avatar URL behavior, page, and account page.

By coder.
2026-08-27 09:53:36 -07:00
Chris Troutner 01a16769bd Implement Set Bio (0x6d05) client write path
Add Memo set-bio action, Set Bio page controller, Set Bio React view,
account bio display, and acceptance step handlers. Includes unit tests.

By coder.
2026-08-27 07:50:33 -07:00
Chris Troutner 3f5c8f809a Display like counts on feed, profile, and thread
- Add testable RecentFeedPage, ProfilePage, and ThreadPage controllers.
- Wire acceptance handlers for the like-count-display feature.
- Render like counts on the profile page with a read-only LikeButton.
- Add unit tests for the new display page services.

By coder.
2026-08-26 19:48:37 -07:00
Chris Troutner c168c652de Creating mono-repo from psf-memo-client 2026-08-26 09:10:39 -07:00