- Add newsletter fields to ComposerUiState (enabled, newsletters list,
selected newsletter, sendAsNewsletter, emailSegment, subscriber count,
confirmation dialog state)
- Load newsletter data on init when newsletter features are enabled
- Add newsletter options in publish dropdown: send-as-newsletter switch,
newsletter picker (radio buttons), email segment picker (All/Free/Paid)
- Show warning about irreversible email send with subscriber count
- Change publish button to tertiaryContainer color and email icon when
newsletter sending is active
- Add NewsletterConfirmationDialog requiring "WYSLIJ" typed input to
confirm, with summary of newsletter name, segment, count, and title
- Pass newsletter slug and email segment through to PostRepository
- Store newsletter slug in LocalPost for offline queue support
- Extend FeedPost with fileUrl and fileName fields
- Parse mobiledoc file cards in FeedViewModel.extractFileCardFromMobiledoc()
- Map LocalPost file fields to FeedPost in toFeedPost()
- Create FileAttachmentCard composable with file type icon colors and tap-to-download
- Integrate file card into PostCardContent (FeedScreen) and DetailScreen
- Add file card support to MobiledocBuilder with Ghost's native file card format
- Add file card tests to MobiledocBuilderTest
- Add file state fields to ComposerUiState (fileUri, fileName, fileSize, fileMimeType, uploadedFileUrl)
- Add addFile()/removeFile() methods to ComposerViewModel with 50MB size validation
- Add file picker button and FileAttachmentComposerCard in ComposerScreen
- Update PostUploadWorker to upload files via repository.uploadFile() and include in mobiledoc
- Add "Newsletter" section after Content/Tags section
- Switch to enable/disable newsletter features per account
- Info text explaining the toggle's effect on composer
- Best-effort API validation when toggling ON (fetches newsletters count)
- Animated validation status display
- Create NewsletterModels.kt with GhostNewsletter and NewslettersResponse
- Add getNewsletters() endpoint to GhostApiService
- Add optional newsletter/emailSegment query params to createPost/updatePost
- Create NewsletterPreferences for per-account newsletter toggle
- Add fetchNewsletters() and fetchSubscriberCount() to PostRepository
- Pass newsletter params through PostRepository to API service
- Add Robolectric tests for NewsletterPreferences
- Create FileModels.kt with FileUploadResponse and UploadedFile data classes
- Add uploadFile() multipart endpoint to GhostApiService
- Add uploadFile(uri) method to PostRepository following the same pattern as uploadImage()
StatsViewModel fetches tags from TagRepository, computes most used tag
and posts-without-tags count. StatsScreen shows "Tags" section with
horizontal progress bars (LinearProgressIndicator per tag, colored by
accent_color), most used tag, total tags, and posts without tags count.
MemberDetailScreen shows scrollable profile: large avatar header with name
and email, 3 quick stat tiles (status, open rate, emails sent), subscription
details for paid members (tier, price, renewal date, cancellation status),
activity section (joined date, last seen, geolocation), newsletters list
with read-only checkboxes, labels as FlowRow of AssistChips, email activity
with open rate progress bar, and member notes.
FeedViewModel fetches tags on refresh(), takes top 10 by post count.
FeedScreen shows LazyRow of FilterChip below status filter: "All tags"
first, then popular tags with post counts. Tapping filters posts by tag.
Post cards now show tags in compact labelSmall format joined by dots.
When more than one account is configured, display an informational
AssistChip at the top of the Composer showing the active blog name
and site icon. Uses SiteMetadataCache for the blog title, falls back
to account name. Non-clickable, only shown for disambiguation.
MembersViewModel manages members list state with loading, pagination,
filter (All/Free/Paid), and debounced search. MembersScreen shows
TopAppBar with total count, search field, segmented filter buttons,
and LazyColumn with member rows (avatar via Coil or colored initial,
name, email, open rate progress bar, relative time, PAID/NEW badges).
Add Routes.MEMBERS and Routes.MEMBER_DETAIL to NavGraph (not in
bottomBarRoutes). Wire "See all members" button from Stats screen.
Replace account name with blog title from SiteMetadataCache in the Feed
TopAppBar. Show site icon (24dp, circular) before the title. Truncate
blog name to 20 characters with ellipsis. Falls back to account name
or "Swoosh" if no cached site data exists.
TagsViewModel manages tag CRUD state. TagsScreen shows searchable list
of OutlinedCards with accent dot, name, count, description. Edit mode
supports name, slug (read-only), description, accent_color hex,
visibility radio. Wired into NavGraph via Routes.TAGS and accessible
from Settings screen via "Tags" row.
Display cached Ghost site metadata (logo/icon, title, description, URL,
version, locale) in a card above the Current Account section. Add "Open
Ghost Admin" button that launches the blog's admin panel in browser.
Show version warning banner if Ghost version is older than v5.
Add PagesViewModel with CRUD operations and edit/create state management.
Add PagesScreen with dual-mode UI (list with long-press context menu and
editor with title/content/slug/status fields). Wire navigation from
Settings via "Static Pages" row. Pages use slide-in-horizontal transition
consistent with other detail screens.
StatsViewModel now fetches members via MemberRepository and computes
MemberStats. StatsScreen shows a 2x3 grid of ElevatedCard tiles when
memberStats is available: Total, New this week, Open rate, Free, Paid,
MRR. Includes animated counters and a "See all members" navigation button.
Member fetch failure is non-fatal (tiles simply hidden).
After successful connection test, fetch Ghost /site/ endpoint to get
blog name, description, icon, and version. Show a confirmation card
with site details before completing setup. Warn if Ghost version < 5.
Cache site metadata per account via SiteMetadataCache. Falls back to
existing behavior if site fetch fails.
ComposerViewModel fetches available tags from TagRepository on init,
filters suggestions as user types, supports addTag/removeTag. ComposerScreen
shows tag input field with dropdown suggestions (name + post count),
"Create new" option, and FlowRow of InputChip tags with close icons.
MemberRepository follows the same pattern as PostRepository: Context constructor,
AccountManager, ApiClient. Includes fetchMembers (paged), fetchMember (single),
fetchAllMembers (all pages, max 20), and getMemberStats (pure function computing
total/free/paid/newThisWeek/avgOpenRate/MRR). Comprehensive tests for getMemberStats.
Follows PostRepository pattern with AccountManager-based auth,
Dispatchers.IO coroutine context, and Result<T> return types.
Exposes fetchPages, createPage, updatePage, deletePage methods
plus getBlogUrl for constructing page URLs in the UI.
Introduce GhostPage, PagesResponse, PageWrapper data classes for
Ghost CMS static pages. Add CRUD endpoints (getPages, createPage,
updatePage, deletePage) to GhostApiService. Include comprehensive
unit tests for serialization and default values.
SharedPreferences-based cache for GhostSite metadata keyed by account ID.
Supports save/get/getVersion/remove operations with Gson serialization.
Includes Robolectric tests for round-trip, overwrite, multi-account
isolation, and removal.
Add MemberModels.kt with GhostMember, MemberLabel, MemberNewsletter,
MemberSubscription, SubscriptionPrice, and SubscriptionTier data classes.
Add getMembers() and getMember() endpoints to GhostApiService.
Add comprehensive JSON parsing tests for all member model types.
Add TagModels.kt with GhostTagFull, TagsResponse, TagWrapper, TagCount
data classes for full Ghost tag management. Add getTags, getTag,
createTag, updateTag, deleteTag endpoints to GhostApiService.
Add GhostSite data class for Ghost CMS site metadata (title, description,
logo, icon, accent color, URL, version, locale). Add getSite() endpoint
to GhostApiService. Include unit tests for Gson deserialization and
version parsing.
- Bottom tabs: Home / Stats / Settings (was Home / Search / Settings)
- Search icon back in feed top bar
- Stats screen: no back button, tab-style fade transitions
- Removed Stats link from Settings screen
StaggeredItem wrapped every list item in AnimatedVisibility with
slideInVertically + fadeIn + LaunchedEffect delays, causing jittery
scrolling due to excessive recompositions and layout passes.
- Composer: auto-focus with keyboard, send button in top-right with dropdown
(Publish/Draft/Schedule), smaller 120dp image thumbnails with fullscreen preview
- Navigation: bottom tab bar (Home/Search/Settings), hidden on detail screens
- Share now copies link to clipboard instead of opening share sheet
- Fix: pinned label no longer shows twice
- Fix: drafts now appear in feed
- Fix: schedule picker blocks past dates, no more NPE crash
- Animations: snappier springs (1500f stiffness), shorter tweens (150-200ms),
@Immutable on data classes, Coil crossfade 150ms with cache config,
LazyColumn contentType for better reuse
- F4: AnimatedContent with expand/shrink transitions on "Show more" text
- F6: Pulsing alpha animation on queue chip when status is UPLOADING
- F10: Staggered slideInHorizontally + fadeIn entrance for account switcher items