Skip to content

Add pinned statuses to profile#32

Merged
cbl merged 1 commit into
mainfrom
feat/pinned
Apr 21, 2026
Merged

Add pinned statuses to profile#32
cbl merged 1 commit into
mainfrom
feat/pinned

Conversation

@cbl

@cbl cbl commented Apr 21, 2026

Copy link
Copy Markdown
Member

Adds pinned-statuses support to profile pages (read-side). Most-recently-pinned post renders as a full Status with a "Pinned" indicator; additional pins (up to 5 total, per Mastodon) appear in a compact row below. Plus two polish fixes to Status.vue spacing that surfaced during review.

Pinned section

  • Primary + overflow pattern. The newest pin is rendered as a full Status (readable long-form text). If there are 2–5 pins, the rest appear as small preview cards below. Avoids the 5-stacked-posts scroll problem while respecting user curation.
  • Mobile (< md): horizontal snap-scroll, cards fixed at 220px, left-indented 72px so the first card aligns with the primary pin's text content (under the avatar).
  • Desktop (md+): switches to a 2-column grid. No chevron controls — with max 4 overflow cards, everything fits without scrolling. Matches App Store / Stripe "content row" patterns for wide viewports.
  • Dedupe: pinned IDs filtered out of the chronological timeline so no post appears twice.
  • Only on the default Posts tab (web). Replies/Media tabs stay chronological — pins that are replies show naturally in Replies, etc. Matches Twitter's rule.
  • No empty state noise: if a profile has only pinned posts and no chronological posts, the "No posts yet" state is suppressed.

Added

  • packages/api/src/composables/queries/useAccountData.tsgetAccountPinnedStatuses(acct) calls accounts.statuses.list({ pinned: true, limit: 5 }).
  • packages/api/src/composables/useAccount.ts — extended mobile composable with pinnedStatuses ref, fetched in parallel with recent statuses via Promise.all.
  • packages/api/src/mock/handlers/accounts.ts — mock handler supports ?pinned=true.
  • packages/api/src/mock/fixtures/statuses.ts — jane@ has 3 pins, sarah@ has 1, so overflow-row and single-pin cases are both testable in mock mode.
  • packages/ui/src/components/status/Status.vueisPinned prop. Renders a pushpin-icon header row using the same slot as the reblog indicator.
  • packages/ui/src/components/status/PinnedOverflowCard.vue — compact card (~220px) with pushpin micro-label, 3-line content excerpt, media count indicator. Accepts a class prop so the parent controls width.
  • packages/ui/src/components/status/PinnedSection.vue — coordinates primary + overflow, forwards all Status action events.
  • apps/web/app/components/ProfilePinnedSection.vue — thin wrapper that wires useWebActions (mirror of StatusTimeline.vue's pattern).
  • apps/web/app/pages/@[acct]/index.vue — fetches pinned + chronological in parallel, dedupes, renders both.
  • apps/mobile/src/pages/Profile.vue — dedupe + render; mobile has no tabs so pins sit above the flat list.

Polish fixes to Status.vue

Surfaced while staring at the pinned section, but affect every feed:

  • Symmetric vertical padding. StatusActions's ButtonAction adds pb-2 (8px) for tap-target extension, which meant the visual gap below the action icons was 20px while the gap above the display name was only 12px. Changed the content column from py-3 to pt-3 pb-1 when actions render (preserved pb-3 when hideActions=true for thread ancestors). Top and bottom now both visually 12px.
  • Tighter indicator-to-post gap. The "Pinned" / "X reposted" label was separated from the post header by 12px, making it feel like its own block instead of a caption attached to the post. When hasIndicator is true, content column's top padding drops from pt-3 to pt-1 (4px), matching Twitter/Bluesky's tight caption-style spacing.

Out of scope

  • Pin / unpin action (write-side) — follow-up PR will add "Pin to profile" / "Unpin from profile" to the status actions menu for own posts.
  • Profile tabs on mobile — mobile profile is still a single flat list, pins just render above it.

@cbl cbl merged commit 87e8868 into main Apr 21, 2026
2 checks passed
@cbl cbl deleted the feat/pinned branch April 21, 2026 11:21
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.

1 participant