Skip to content

Render deck cover image + author avatar, @you, and cleanup - #18

Merged
jvsena42 merged 8 commits into
mainfrom
fix/ios-deck-detail-cover
Jun 21, 2026
Merged

Render deck cover image + author avatar, @you, and cleanup#18
jvsena42 merged 8 commits into
mainfrom
fix/ios-deck-detail-cover

Conversation

@jvsena42

Copy link
Copy Markdown
Owner

Summary

Fixes deck-detail rendering across both platforms. The deck cover is an image (Deck.coverImageRef — remote Unsplash URL or homeserver blob), but nothing rendered it: the presentation state only exposed coverEmoji and both UIs drew an emoji box. This surfaces the cover image from the shared ViewModel and renders it natively, adds the author avatar, fixes the owned-author label, and removes an untracked placeholder.

Changes

Shared (DeckDetailViewModel)

  • Expose coverImageUrl (remote) + coverImageBase64 (homeserver blob, fetched once in viewModelScope via MediaRepository and Base64-encoded) on Content.
  • Fetch the author's pubky.app profile and expose authorAvatarUrl.
  • Inject MediaRepository into the VM + Koin binding.

iOS (DeckDetailView / DeckDetailScreen)

  • Render the cover in priority order: remote AsyncImage → blob UIImage from Base64 → accent-soft emoji box (blank-guarded to the title initial → 📚).
  • Owned decks show "@you" only (no pubky subtitle).

Android (DeckDetailScreen / AuthorRow)

  • CoverSection renders the cover via Coil (URL or decoded ByteArray), same fallback.
  • AuthorRow gained isOwned (→ "@you", no pubky line, no Follow button for your own deck) and avatarUrl (Coil avatar over the initial as graceful fallback).
  • Dropped the static "Last studied…" line — it was never tracked.

Cleanup

  • Removed the dead Deck.lastStudiedAt field (always null, never read/written) and its unused string resource.

Notes

  • The cover image now renders on both platforms; Android cover rendering reuses the shared VM-loaded bytes (no second fetch).
  • Avatars that are plain http(s) URLs render via Coil; a pubky:// blob avatar would gracefully fall back to the initial — a possible follow-up.
  • Real "last studied" tracking (via SRS review state) was scoped out by design.

Verification

  • ./gradlew :shared:compileKotlinMetadata :composeApp:compileDebugKotlin — compiles.
  • ./gradlew detektAll — clean.
  • ./gradlew lintSwift — 0 violations.
  • iOS isn't runnable end-to-end yet (Koin bootstrap commented out); iOS verified via SwiftUI previews.

🤖 Generated with Claude Code

jvsena42 and others added 8 commits June 21, 2026 19:22
DeckDetailUiState.Content only carried coverEmoji, so a deck's actual
cover image (Deck.coverImageRef) was never surfaced to the UI. Add
coverImageUrl (remote/Unsplash) and coverImageBase64 (homeserver blob,
fetched via MediaRepository and Base64-encoded for transport) to Content
so native UIs can render the real cover, falling back to the emoji box.

Blob fetching stays in the ViewModel (viewModelScope) per the shared-logic
rule; remote URLs are carried synchronously. Inject MediaRepository into
the VM and its Koin binding.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DeckDetailView drew only an emoji box, so decks with a real cover image
showed a letter/emoji placeholder. Render the cover in priority order:
remote URL (AsyncImage) → homeserver blob (Base64 → UIImage) → the
accent-soft emoji box. The emoji box now blank-guards coverEmoji and
falls back to the title initial, then a book glyph.

Owned decks now read "@you" in the author row (dropping the redundant
pk:… subtitle), matching the decks-list convention.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
For a self-created deck the AuthorRow showed the full pubky plus a Follow
button — you can't follow yourself. Add an isOwned flag: when owned, the
row reads "@you", drops the pubky subtitle, and hides the Follow button,
matching the iOS deck-detail author treatment.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CoverSection drew only the emoji box, so a deck's real cover image (saved
on creation via coverImageRef) never showed. Render the cover from the
shared ViewModel fields in priority order: remote URL → homeserver blob
(Base64 bytes already loaded by the VM, decoded to a ByteArray) → the
accent-soft emoji box. Coil's AsyncImage renders both a URL and a
ByteArray, so no second fetch is needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deck-detail author row only had the pubky initial. Fetch the author's
pubky.app profile (IdentityRepository.fetchProfile) and fold its avatar
URL into Content.authorAvatarUrl so native UIs can render the real
picture, falling back to the initial when absent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
AuthorRow drew only the initial letter. Add an optional avatarUrl: when
set, render the picture via Coil over the initial (which stays underneath
as the placeholder/error fallback while the image loads or if it fails).
DeckDetail passes the author's avatar from the shared state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The deck's "last studied" date was never tracked — Deck.lastStudiedAt was
always null (never read or written) and the OwnedBadgeRow showed a static
"Last studied..." placeholder. Remove the dead field, the unused string
resource, and the placeholder line, leaving just the "In your library"
badge. Real last-studied tracking can be added later via SRS review state.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@jvsena42
jvsena42 enabled auto-merge June 21, 2026 22:58
@jvsena42
jvsena42 merged commit 7a5ef27 into main Jun 21, 2026
2 checks passed
@jvsena42
jvsena42 deleted the fix/ios-deck-detail-cover branch June 21, 2026 23:00
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