Skip to content

feat(user): WI-1.7 boost/undo + repo refactor#85

Merged
veeso merged 38 commits intomainfrom
feat/18-wi-1-7-implement-user-canister-boost-status
May 3, 2026
Merged

feat(user): WI-1.7 boost/undo + repo refactor#85
veeso merged 38 commits intomainfrom
feat/18-wi-1-7-implement-user-canister-boost-status

Conversation

@veeso
Copy link
Copy Markdown
Owner

@veeso veeso commented May 3, 2026

Summary

Implements WI-1.7 Boost / Undo Boost for the user canister and refactors the repository layer used by both user and directory canisters.

WI-1.7 boost feature

  • Status extended with spoiler_text + sensitive; BoostRepository adds find_by_original_uri; StatusRepository gains increment_boost_count / decrement_boost_count.
  • did::user::GetLocalStatus + did::federation::FetchStatus types.
  • federation canister: fetch_status flow (local-only) and update endpoint; user canister adds federation::fetch_status adapter.
  • user::get_local_status query with caller-scoped visibility.
  • boost_status flow (fetch + insert wrapper Status/Boost/FeedEntry + Announce dispatch) and undo_boost flow (delete + Undo(Announce) dispatch).
  • Inbox handlers for Announce and Undo(Announce); outbox/inbox renderers surface boosted_by + original author; feed items now expose viewer liked / boosted flags.
  • End-to-end pocket-ic integration tests for boost / undo_boost.
  • Architecture, sequence diagrams, and Candid interface docs updated.

Repository refactor

  • db_utils::transaction::Transaction primitive (begin / commit / rollback / run) so callers own transaction lifecycle.
  • All 12 repositories (user: block, follower, following, follow_request, liked, profile, status, feed, boost; directory: users, moderators, tombstone) converted from associated-function style to instance-based with optional TransactionId.
  • db_utils::repository::Repository trait with Schema associated type and required oneshot / with_transaction / tx / schema methods, providing a default db() accessor — drops the per-repo db() boilerplate.
  • Boost flow finalised: cross-table writes moved out of BoostRepository into per-flow helpers (boost_status::insert_boost_with_wrapper, undo_boost::delete_boost_with_wrapper); each repo writes only its own table.

Test plan

  • just check_code (fmt + clippy -D warnings)
  • just test — 341 user, 170 directory, 81 db-utils, 39 federation, 4 ic-utils, all passing
  • just build_all — directory/federation/user WASM artefacts rebuild clean
  • just integration_test — pocket-ic suites green, including new boost/undo_boost end-to-end coverage

veeso added 30 commits May 2, 2026 15:45
Reduce boost_status to a todo!() placeholder until Task 11 implements the
flow, dropping the unrelated dead_code annotations and arg-rename diff
from the find_by_original_uri commit.
Wires the existing domain flow as a public `#[ic_cdk::query]` endpoint,
removes the now-dead `expect(dead_code)` guard, and syncs `user.did`
with the new method plus the `spoiler_text`/`sensitive` fields on `Status`.
Sync the mdBook docs to reflect the boost-status implementation:

- interface/types.md: add `spoiler_text` / `sensitive` to Status, plus
  new GetLocalStatus and FetchStatus sections; cross-link from
  BoostStatus to the fetch flow.
- project.md: add `get_local_status` to the User Canister interface
  and `fetch_status` to the Federation Canister interface.
- project/flows.md: replace the Boost Status diagram with the
  fetch_status -> get_local_status -> wrapper insert -> Announce
  pipeline; document the shared snowflake and idempotency.
- architecture.md: add a Boost Flow sub-section mirroring the diagram.
- architecture/database-schema.md: clarify that boosts.id is shared
  with the wrapper status, feed entry, and Announce activity id.
- specs/status.md: note that spoiler_text and sensitive flow into
  wrapper rows when a status is boosted.
- specs/urls.md: document the wrapper status URL as the Announce id.
- activitypub.md: mark Announce / Undo(Announce) as M1 implemented and
  point at boost_status / handle_announce / handle_undo_announce.
Extend FeedItem with `liked` and `boosted` booleans so clients can render
"you liked / you boosted" markers on each feed entry without an extra
round-trip. Both flags are populated in `hydrate_outbox` and
`hydrate_inbox` by querying the viewer's `liked` and `boosts` tables for
the canonical status URI. Inbox `Create(Note)` activities resolve the
note's `id` (or fall back to `{actor}/statuses/{id}` when the note id is
emitted as a bare snowflake).
Centralises transaction lifecycle so repositories no longer drive commits
themselves. Generic over schema; `run` auto-rolls back on Err.
Mirror the block repository shape: `LikedRepository { tx: Option<TransactionId> }`
with `oneshot()` / `with_transaction(tx)` constructors and a private `db()`
helper that picks oneshot vs from_transaction. All four public methods
(`like_status`, `unlike_status`, `is_liked`, `get_liked`) become `&self`.

All 15 call sites across feed, liked/like, liked/unlike, and liked/get_liked
updated to `LikedRepository::oneshot().…`. No behavior change.
Mirrors the canonical pattern from block/liked/profile: oneshot()
constructor for plain calls, with_transaction(tx) for splicing into
externally-driven transactions, and a private db() helper that
dispatches to the right WasmDbmsDatabase. All public methods become
&self; record_to_follower stays a private associated fn. No internal
transactions in this repo.

Updated all FollowerRepository call sites to ::oneshot().METHOD(...).
The accept_follow_transaction raw DBMS_CONTEXT block is intentionally
left untouched and will migrate to Transaction::run in a follow-up
once FollowRequest gets the same refactor.
Convert FollowingRepository to instance-based pattern with oneshot()
and with_transaction() constructors plus a private db() helper. The
internal transaction in update_status is removed: callers must now
wrap the call in Transaction::run to preserve atomicity across the
underlying delete + insert.

handle_incoming::handle_accept and the corresponding tests now wrap
update_status in Transaction::run::<_, _, _, CanisterError>(Schema, ..),
preserving the existing error mapping. All other call sites switch to
FollowingRepository::oneshot().

Schema gains Copy so it can satisfy the Transaction::run bound.
veeso added 8 commits May 3, 2026 10:10
Convert UserRepository to an instance-based struct exposing
oneshot()/with_transaction() constructors, matching the canonical
repository pattern used by status/boost/tombstone/moderators. The repo
now exposes &self methods only and never opens or commits a transaction
internally — callers wrap multi-step writes in Transaction::run.

Update every directory caller (api flows, sign_up/delete_profile state
machines, test_utils, search_profiles) to use UserRepository::oneshot()
for single-row reads and writes.

Add a CanisterError::Internal variant so transaction-aware tests can
inject a sync error and assert rollback.

Add Copy to the directory Schema (matches user-canister Schema), so
callers can pass it to Transaction::run.

New tests cover: tx commit, tx rollback for sign_up, atomic
sign_up + set_user_canister inside one tx, rollback of set_user_canister
when the closure errors, remove_user inside a tx, and rollback of
remove_user.
Centralises `feed` table writes behind a single repository so subsequent
status / boost / inbox refactors can route their inserts through one
lifecycle-aware entry point. Reads are still served by the existing
`read_feed` flow, which performs the cross-table hydration join.

Follows the canonical `oneshot` / `with_transaction` pattern: callers own
transaction lifecycle, the repo never commits or begins transactions.
Tests cover both source variants, deletion, transactional commit and
rollback semantics.
- Add `db_utils::repository::Repository` trait providing default `db()`
  accessor; impls supply schema/oneshot/with_transaction/tx.
- Migrate all 12 user/directory repositories onto the trait, dropping
  inherent oneshot/with_transaction/db boilerplate; callers import the
  trait.
- Move boost orchestration helpers (`insert_boost_with_wrapper`,
  `delete_boost_with_wrapper`) next to the flows that drive them
  (`boost_status.rs` / `undo_boost.rs`); tests follow.
- Drop now-reachable `#[allow(dead_code)]` from feed/status repos.
@cocogitto-bot
Copy link
Copy Markdown

cocogitto-bot Bot commented May 3, 2026

✔️ 7054634...6775cea - Conventional commits check succeeded.

@veeso veeso merged commit 4821296 into main May 3, 2026
3 checks passed
@veeso veeso deleted the feat/18-wi-1-7-implement-user-canister-boost-status branch May 3, 2026 19:12
@veeso veeso linked an issue May 3, 2026 that may be closed by this pull request
20 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WI-1.7: Implement User Canister - boost status

1 participant