Files
holy-flavor-rater/community-redesign.md
T
2026-06-16 19:59:52 +02:00

6.6 KiB

Community Tab Redesign

The Community tab is the oldest part of the site and doesn't fit the robust, professional design of the rest. This document tracks the redesign.

Starting state

frontend/src/pages/CommunityFeed.tsx — a paginated feed of ratings from followed users only, with a single sidebar card ("Circle's Top Rated"). Replies are hidden behind a per-card collapse. Loading is a bare center spinner.

Core problems

  • Cold start is dead. No follows → empty feed → no reason to return. The backend ratings/feed/ is gated on the follow graph and returns nothing for users who follow no one.
  • Single content type. Only ratings. No new-flavor drops, profile comments, follow events, or milestones.
  • No engagement loop. No reactions/likes. Replies buried in a collapse. The feed is a read-only wall.
  • No filtering/sort. Can't sort recent/top or filter by category, even though the payload already carries category_name/category_slug.
  • Sparse sidebar. One card wastes the 4-column rail.

Phase 1 — frontend-only (DONE)

No backend changes. Reuses the existing GET /api/ratings/recent/ endpoint (global, AllowAny, 10 newest ratings with a comment).

  • Following | Discover tabs. Discover surfaces the whole community's newest ratings, so the page is useful even when you follow nobody — fixes the cold-start hole. Discover is fetched lazily (enabled: tab === 'discover').
  • Skeleton loader (FeedSkeleton) replaces the center spinner — matches the professional look of the rest of the app.
  • Inline reply preview. The latest reply shows compact under each card; click expands the full thread + compose box. Replies feel like conversation, not archive.
  • Smarter empty states. Empty Following nudges to Discover (button switches tab); empty Discover nudges to explore flavors.
  • i18n EN + DE keys added under community.*.

Files touched

  • frontend/src/api/queries/useCommunityFeed.ts — added useRecentRatings.
  • frontend/src/pages/CommunityFeed.tsx — tabs, skeleton, inline reply preview, empty-state logic.
  • frontend/src/i18n.ts — tabFollowing, tabDiscover, discoverSubtitle, discoverEmpty, discoverEmptyHint, browseDiscover (EN + DE).

Phase 2 — needs backend

1. Reactions (highest engagement lever) — DONE

Lightweight 👍 / emoji on ratings. Social proof, drives return visits.

  • New rating_reaction table (user_id, rating_id, kind, created_at; unique on user_id + rating_id + kind). Created in db.rs::ensure_schema (idempotent CREATE TABLE IF NOT EXISTS + index on rating_id).
  • Endpoints: POST /api/ratings/{id}/react/, DELETE /api/ratings/{id}/react/. Allow-listed kind, rate-limited (60/min/user), idempotent on the unique index.
  • RatingOut carries reactions (kind → count) + my_reactions (caller's own). Threaded through build_ratings(viewer_id) via the OptionalUser extractor.
  • Frontend: ReactionBar on each feed card; optimistic toggle, server-reconciled.

Files touched

  • backend-rs/src/entities/rating_reaction.rs (new), entities/mod.rs.
  • backend-rs/src/db.rs (table + index), throttle.rs (REACTION rate), dto.rs (reactions/my_reactions), service.rs (load_reactions, build_ratings viewer param), routes/ratings.rs (react/unreact), plus call-site updates in routes/users.rs, routes/admin.rs.
  • frontend/src/components/ReactionBar.tsx (new), frontend/src/pages/CommunityFeed.tsx, api/queries/useCommunityFeed.ts, frontend/src/i18n.ts.

2. Discover feed with sort + pagination — DONE

recent/ is fixed at 10 and unpaginated. Discover is now a real feed.

  • New GET /api/ratings/discover/ (AllowAny) accepts ?sort=recent|top, ?category=<slug>, ?page=. Quality bar kept (non-empty comment only). top orders by score desc then recency; category resolves slug → flavor ids.
  • Frontend: sticky filter bar — Recent | Top rated sort chips + All plus a chip per category (reuses useCategories). Discover now paginates like Following; page resets on tab/sort/category change.

Files touched

  • backend-rs/src/routes/ratings.rs (discover handler + route).
  • frontend/src/api/queries/useCommunityFeed.ts (useDiscoverFeed), frontend/src/api/keys.ts (communityDiscover), frontend/src/pages/CommunityFeed.tsx (filter bar, discover pagination), frontend/src/i18n.ts (sortRecent, sortTop, allCategories).

3. Who-to-follow — DONE

Sidebar card suggesting active raters the user doesn't follow yet.

  • GET /api/users/suggested/ — up to 5 most-active raters (by rating count), excluding self + already-followed. Returns UserOut (reuses build_users), re-sorted to the activity ranking after the id-ordered load.
  • Frontend: SuggestedFollows card (avatar + name + follower count + Follow button), optimistic — drops the user from the list on follow, reverts on error.

Files touched

  • backend-rs/src/routes/users.rs (suggested handler + route).
  • frontend/src/api/queries/useCommunityFeed.ts (useSuggestedUsers), frontend/src/pages/CommunityFeed.tsx (SuggestedFollows sidebar card), frontend/src/i18n.ts (whoToFollow, follow, followFailed, followerCount).

4. Richer activity types — DONE

Make the feed alive when friends are quiet.

  • New GET /api/activity/ (AllowAny, paginated): a unified, newest-first stream of new_flavor drops + milestone events. No persistent event table — items are synthesized on read from flavor.created_at and, for milestones, the timestamp of the rating that crossed a threshold (25/50/100/250/500/1000; one item per flavor at its highest crossed threshold). Candidate set is bounded (latest 40 drops + flavors ≥25 ratings), built once and paged in memory.
  • Frontend: third Activity tab with its own card style (drop vs. milestone icon), paginated like the others.

Files touched

  • backend-rs/src/dto.rs (ActivityOut), service.rs (build_activity), routes/activity.rs (new), routes/mod.rs (mount).
  • frontend/src/api/queries/useCommunityFeed.ts (useActivityFeed), frontend/src/api/keys.ts (communityActivity), frontend/src/pages/CommunityFeed.tsx (Activity tab + renderActivityCard), frontend/src/i18n.ts (tabActivity, activitySubtitle, activityEmpty, activityEmptyHint, newDropLabel, milestoneLabel, milestoneText).

Suggested order

Phase 2.1 (reactions) and 2.2 (Discover sort/filter) give the most UX lift per unit of backend work. 2.3 and 2.4 follow. All of Phase 2 is now complete.