Swoosh/CLAUDE.md
Paweł Orzech 2cb4ad7953
docs: update README with screenshots and new features
Add app screenshots (feed, stats, newsletter, settings) and update
README and CLAUDE.md to reflect v0.3.0 changes: newsletter integration,
static pages, members management, tag management, pin posts, 4-tab
bottom nav, 31 test classes, and Media3 in tech stack.
2026-03-20 09:39:42 +01:00

5.3 KiB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Project Overview

Swoosh is an Android microblogging client for Ghost CMS. It provides offline-capable post creation, image uploads, link previews, and scheduled publishing via Ghost's Admin API.

Build & Test Commands

./gradlew assembleDebug          # Build debug APK
./gradlew assembleRelease        # Build release APK (ProGuard enabled)
./gradlew test                   # Run all unit tests
./gradlew app:testDebugUnitTest  # Run unit tests for debug variant
./gradlew test --tests "*.MobiledocBuilderTest"  # Run a single test class
./gradlew test --tests "*.MobiledocBuilderTest.testBasicTextPost"  # Run a single test method

Robolectric is used for tests that need Android framework classes. Unit tests include Android resources (unitTests.isIncludeAndroidResources = true).

Architecture

MVVM with Repository pattern, single-module Gradle project.

Package: com.swoosh.microblog

  • data/api/ — Retrofit service (GhostApiService), JWT auth (GhostJwtGenerator, GhostAuthInterceptor), and ApiClient singleton with dynamic base URL
  • data/db/ — Room database with LocalPost entity and LocalPostDao
  • data/model/ — Three model layers: GhostPost (API), LocalPost (Room entity), FeedPost (UI display). Additional models: PostStats, OverallStats, GhostAccount, GhostNewsletter, GhostPage, GhostMember. Enums: PostStatus, QueueStatus, PostFilter, SortOrder
  • data/repository/PostRepository coordinates local DB and remote API; OpenGraphFetcher parses link previews via Jsoup
  • data/ (root utilities) — AccountManager (multi-account, up to 5), CredentialsManager, FeedPreferences, HashtagParser, MobiledocBuilder, ShareUtils, UrlNormalizer
  • ui/animation/SwooshMotion shared animation specs (bouncy, snappy, gentle, quick)
  • ui/components/ — Reusable composables: AnimatedDialog, ConfirmationDialog, PulsingPlaceholder
  • ui/feed/ — Post feed with search, filtering (All/Published/Draft/Scheduled), sorting
  • ui/composer/ — Post creation/editing with image uploads, link previews, hashtags, scheduling
  • ui/detail/ — Full post view with pin toggle and animated delete dialog
  • ui/members/ — Ghost members/subscribers management
  • ui/newsletter/ — Newsletter configuration, subscriber count, newsletter list
  • ui/pages/ — Static pages CRUD (create, edit, delete, publish)
  • ui/preview/ — HTML post preview
  • ui/stats/ — Statistics dashboard with animated counters (total posts, word counts, reading time, tags)
  • ui/settings/ — Settings, account management, theme toggle, tags toggle, disconnect
  • ui/setup/ — Initial configuration wizard and add-account flow
  • ui/tags/ — Tag management and filtering
  • ui/theme/ — Material 3 theming with ThemeMode (Light/Dark/System), ThemeViewModel, ThemePreferences
  • ui/navigation/ — Compose Navigation graph with bottom nav (Home, Newsletter, Stats, Settings)
  • worker/PostUploadWorker (WorkManager) handles offline queue with exponential backoff

Key data flow: Posts are saved to Room first → queued for upload → PostUploadWorker syncs to Ghost API when network is available.

Technical Details

  • Auth: Ghost Admin API keys are split into id:secret, secret is hex-decoded for HS256 JWT signing (5-min expiry)
  • Content format: Posts use Ghost's mobiledoc JSON format, built by MobiledocBuilder (supports text paragraphs and bookmark cards)
  • Credentials: Stored in EncryptedSharedPreferences (AES256-GCM) via CredentialsManager
  • API client: Base URL is configured at runtime during setup; ApiClient rebuilds Retrofit instance when URL changes
  • Multi-account: AccountManager supports up to 5 Ghost accounts with UUID-based IDs, per-account credentials, and migration from legacy single-account format
  • Avatars: Fetched from Ghost post authors (posts[0].authors[0].profile_image), not /users/me/ (which returns 404 for integrations)
  • Permissions: INTERNET, ACCESS_NETWORK_STATE, CAMERA
  • ProGuard: Release build suppresses missing com.google.errorprone.annotations (pulled in by Google Tink via EncryptedSharedPreferences)
  • Min SDK 26, Target/Compile SDK 34, Kotlin 1.9.22, Java 17

Versioning

Version is defined in app/build.gradle.kts (versionCode and versionName).

  • Format: Semantic versioning MAJOR.MINOR.PATCH (e.g., 0.2.0)
  • versionCode: Integer, increment by 1 on every release (used by Google Play)
  • versionName: Human-readable, shown in Settings screen

When to bump:

  • PATCH (0.2.0 → 0.2.1): Bug fixes, small tweaks, no new features
  • MINOR (0.2.0 → 0.3.0): New features, UI changes, significant improvements
  • MAJOR (0.x → 1.0): First stable public release

Current: versionName = "0.3.0", versionCode = 3

Process: When making a release commit, bump both versionCode (+1) and versionName in app/build.gradle.kts. Always bump version when creating a release build or PR.

Assets

Screenshots for README are in pics/ — named by screen (feed.png, stats.png, newsletter.png, settings.png).