Conversation
This commit adds previews to all composables and screens in the project. This will make it easier to develop and test the UI components in isolation. I have also removed the default empty lambda parameters from the composables as requested.
This commit moves all the previews from `commonMain` to `androidMain` to avoid issues with the build. It also removes the `@Preview` annotations from the composables in `commonMain`.
This commit moves all the previews from `commonMain` to `androidMain` to avoid issues with the build. It also removes the `@Preview` annotations from the composables in `commonMain`. This also adds dark and light theme previews to all components and screens.
WalkthroughThis change removes a legacy preview file and introduces a comprehensive, modularized preview infrastructure for Compose UI components and screens in the Android app. It creates dedicated preview files for each UI element, provides fake data and string providers, and implements light/dark theme wrappers. Several UI components are also updated for improved styling and composability. Key screen composables are refactored to separate state management from UI rendering. Changes
Sequence Diagram(s)Preview Wrapper and Component Preview InteractionsequenceDiagram
participant PreviewFunction as PreviewFunction
participant ReplyRadarPreviewWrapper
participant FakeProviders as FakeProviders/Theme
participant UIComponent as UIComponent
PreviewFunction->>ReplyRadarPreviewWrapper: Call with darkTheme param & content lambda
ReplyRadarPreviewWrapper->>FakeProviders: Provide fake strings, notification manager
ReplyRadarPreviewWrapper->>FakeProviders: Set MaterialTheme (light/dark)
ReplyRadarPreviewWrapper->>UIComponent: Render content lambda (component preview)
Refactored Screen Composable StructuresequenceDiagram
participant ScreenRoot as ScreenRoot (e.g., SettingsScreenRoot)
participant ViewModel
participant ScreenUI as Stateless UI Composable
ScreenRoot->>ViewModel: Collect state
ScreenRoot->>ScreenUI: Pass state, event handlers, appVersion
ScreenUI->>ScreenUI: Render UI based on state
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
Note ⚡️ Unit Test Generation is now available in beta!Learn more here, or try it out under "Finishing Touches" below. 📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (4)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 7
🧹 Nitpick comments (25)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (2)
115-120: Background is applied after padding – swap modifier order
Modifier.padding().background()draws the background inside the padded area, leaving the top-padding region transparent.
If the intent is a full-width surface stripe (incl. padding), call.background()first, then.padding().- Row( - modifier = Modifier - .fillMaxWidth() - .padding(top = paddingSmall) - .background(colorScheme.surface), + Row( + modifier = Modifier + .fillMaxWidth() + .background(colorScheme.surface) + .padding(top = paddingSmall),
171-174: Same padding/background ordering issue inReminderTextrowFor consistency with the fix above, invert the order here as well so the surface colour covers the whole width.
- modifier = Modifier - .fillMaxWidth() - .background(colorScheme.surface), + modifier = Modifier + .fillMaxWidth() + .background(colorScheme.surface),(also add padding if required).
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/Shared.kt (1)
121-206: Capturenowonce to keep relative timestamps stableEach
System.currentTimeMillis()call is evaluated separately, so the deltas between items are slightly off.
For deterministic previews (and easier snapshot tests) capture the current time once:-val previewActivityLogItems = listOf( +private val now = System.currentTimeMillis() +val previewActivityLogItems = listOf( @@ - createdAt = System.currentTimeMillis() - 1_000_000L + createdAt = now - 1_000_000L @@ - createdAt = System.currentTimeMillis() - 2_000_000L + createdAt = now - 2_000_000L # …repeat for the remaining entries…composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt (1)
50-53: UseonSurfaceinstead ofonBackgroundfor content on a surface
Boxis now painted withcolorScheme.surface, so the title text should useonSurfacefor guaranteed contrast.- color = colorScheme.onBackground + color = colorScheme.onSurfacecomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRoundedCornerPreview.kt (2)
14-15: Enable background for clearer previewsWithout
showBackground = true, the shape is rendered over the transparent IDE canvas. Adding it improves readability and catches unexpected clipping issues early.-@Preview +@Preview(showBackground = true)
20-27: Addclipto make shape effective for children
Modifier.background(shape = …)only paints the background; it does not clip nested content. In real usage you normally want both paint + clip to avoid rectangular hit-areas.- Box( - modifier = Modifier - .size(100.dp) - .background( - color = colorScheme.primary, - shape = ReplyRoundedCorner() - ) - ) + Box( + modifier = Modifier + .size(100.dp) + .clip(ReplyRoundedCorner()) // ✅ clip first + .background( + color = colorScheme.primary, + shape = ReplyRoundedCorner() + ) + )Apply the same change in the dark-theme preview below for consistency.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyButtonPreview.kt (1)
25-30: Remove redundantmodifier = Modifierargument
ReplyButtonalready defaultsmodifiertoModifier; passing it explicitly adds noise.- ReplyButton( - modifier = Modifier, - text = "Button", - onClick = {} - ) + ReplyButton( + text = "Button", + onClick = {} + )Apply the same micro-cleanup in the dark preview for parity.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyNotificationPermissionDialogPreview.kt (1)
8-19: Show dialog background for visual accuracyDialogs often rely on the scrim for context; enabling
showBackgroundmakes the preview closer to runtime appearance.-@Preview +@Preview(showBackground = true)Do the same for the dark-theme preview below.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTogglePreview.kt (1)
14-31: Usename/uiModeparameters to make previews distinguishable in the IDE.Both previews are rendered with
showBackground = true, but without anameoruiModethey appear in the preview pane with identical labels and default (light-theme) metadata. Consider:@Preview( name = "ReplyToggle – Light", showBackground = true, )and for dark:
@Preview( name = "ReplyToggle – Dark", uiMode = UI_MODE_NIGHT_YES, showBackground = true, )This makes it obvious which variant you are inspecting and avoids manual theme switching.
Also applies to: 33-50
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyProgressPreview.kt (1)
15-30: Minor: give the progress indicator some fixed space to avoid zero-height warnings.When a
Boxwithout explicit size hosts an indeterminateReplyProgress, the preview engine sometimes logs “layout with infinite constraints” warnings and shows a clipped indicator. A tiny tweak keeps the preview noise-free:- Box( - modifier = Modifier - .background(MaterialTheme.colorScheme.background) - .padding(16.dp), + Box( + modifier = Modifier + .size(72.dp) // any reasonable constant + .background(MaterialTheme.colorScheme.background) + .padding(16.dp),Not critical, just keeps the preview canvas predictable.
Also applies to: 32-47
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListPreview.kt (1)
19-28: Allow the list to occupy full height for a scrollable realistic preview.
ReplyListcontains aLazyColumn. Without a size constraint its height becomes unconstrained inside the preview, so only the first few items are laid out and scrolling cannot be tested. Wrapping theBox(or theReplyListdirectly) withfillMaxSize()makes the preview closer to runtime behaviour:- Box( - modifier = Modifier - .background(MaterialTheme.colorScheme.background) - ) { + Box( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) {Same applies to the dark variant below.
Also applies to: 37-46
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTimestampInfoPreview.kt (1)
20-29: Small duplication: extract a private helper to reduce copy-paste.Both light and dark previews differ only in the
darkThemeflag. Extracting the shared body avoids duplication noise:@Composable private fun ReplyTimestampInfoPreviewImpl(darkTheme: Boolean) { ReplyRadarPreviewWrapper(darkTheme) { Column(Modifier.background(MaterialTheme.colorScheme.surface)) { ReplyTimestampInfo( state = ReplyBottomSheetState(reply = Replies.OnTheRadar.reply1) ) } } } @Preview(name = "TimestampInfo – Light", showBackground = true) @Composable fun ReplyTimestampInfoPreview() = ReplyTimestampInfoPreviewImpl(false) @Preview(name = "TimestampInfo – Dark", uiMode = UI_MODE_NIGHT_YES, showBackground = true) @Composable fun ReplyTimestampInfoDarkPreview() = ReplyTimestampInfoPreviewImpl(true)Not mandatory, but it keeps future tweaks DRY across both variants.
Also applies to: 39-48
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/fakes/PreviewStringsFake.kt (1)
7-127: Consider automating interface-sync to avoid manual drift.The object hard-codes >120 string properties. Any addition/removal in the
Stringsinterface will silently break the compiler until this file is manually updated, which is easy to overlook.
Generating the fake implementation from the realStringsinterface (e.g. KSP / kapt, KotlinPoet, or a simple gradle task) would erase that maintenance burden and guarantee compile-time alignment.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/FloatingActionButtonPreview.kt (1)
23-26: Pass only the necessary parameters in previews.
FloatingActionButtonalready obtainsMaterialTheme.colorSchemeinternally. Supplying it explicitly adds noise to the preview and can desynchronise if the implementation later derives additional colours from the theme.- FloatingActionButton( - onIntent = {}, - colorScheme = MaterialTheme.colorScheme - ) + FloatingActionButton(onIntent = {})composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/SettingsScreenPreview.kt (1)
20-30: Expose variant previews for other locales / loading states.The Settings screen supports language & loading combinations, yet only the happy-path (English, not loading) is previewed. Adding a second preview with
isLoading = trueand another with a non-default language would deliver faster visual validation of edge cases.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyConfirmationDialogPreview.kt (1)
17-28: Avoid suppressing the dismiss callback inside confirm.
ReplyConfirmationDialoginternally invokesonDismissafteronConfirm. Passing empty lambdas for both in a preview is fine, but make sure future real usages don’t mistakenly double-dismiss or ignore the outeronDismiss. No change required—just flagging the pattern.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyReminderPreview.kt (1)
14-14: Extract duplicate time calculation to reduce code duplication.Both preview functions calculate the current time identically. Consider extracting this to a shared variable at the file level to follow DRY principles.
+private val previewCurrentTime = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) + @Preview(showBackground = true) @Composable fun ReplyReminderPreview() { - val now = Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()) - ReplyRadarPreviewWrapper( darkTheme = false ) { // ReminderText wasn't showing in Preview because the icon row was collapsing the layout. ReplyReminder( - selectedTime = now.time, - selectedDate = now.date, + selectedTime = previewCurrentTime.time, + selectedDate = previewCurrentTime.date,Also applies to: 33-33
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/PlatformDatePickerPreview.kt (1)
17-18: Consider consistency in string resource access.This preview directly uses
PreviewStringsFakeproperties while other previews access strings throughLocalReplyRadarStrings.currentprovided byReplyRadarPreviewWrapper. While both approaches work, using the LocalReplyRadarStrings pattern would be more consistent with the rest of the preview infrastructure.However, since
PlatformDatePickerlikely requires these strings as direct parameters rather than accessing them internally, the current approach is acceptable and may be necessary.Also applies to: 33-34
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarErrorPreview.kt (1)
14-80: Good preview coverage with room for minor improvements.The previews effectively cover the ReplyRadarError component states. Consider these enhancements:
- More descriptive function names: Instead of
Preview1/Preview2, use names likeDefaultErrorPreview/CustomErrorPreview- Extract common Box setup: The repeated Box with surface background could be extracted to reduce duplication
Apply this pattern for more descriptive naming:
-fun ReplyRadarErrorPreview1() { +fun ReplyRadarErrorDefaultPreview() {-fun ReplyRadarErrorPreview2() { +fun ReplyRadarErrorCustomMessagePreview() {composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetContentPreview.kt (1)
11-87: Comprehensive bottom sheet preview coverage!The previews effectively cover the key interaction modes:
- CREATE mode for new reply creation
- EDIT mode with existing reply data
- Both light and dark theme variants
- Proper use of shared preview data
Consider more descriptive function names (e.g.,
CreateModePreview,EditModePreview) to immediately convey the mode being demonstrated.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTextFieldPreview.kt (1)
10-64: Solid text field preview implementation!The previews provide good coverage of the ReplyTextField states:
- Empty and populated text scenarios
- Light and dark theme variants
- Proper use of shared preview data and fake strings
- Consistent preview wrapper usage
Like the other preview files, consider more descriptive function names (e.g.,
PopulatedTextFieldPreview,EmptyTextFieldPreview) for better clarity.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetActionsPreview.kt (2)
53-53: Remove redundantreply = nullparameter in EDIT mode previews.In EDIT mode previews, you're already setting
reply = Replies.OnTheRadar.reply1in the state object, making the separatereply = nullparameter redundant and potentially confusing.- reply = null,Also applies to: 102-102
11-108: Consider reducing code duplication with a shared preview helper.The four preview functions follow an identical structure with only theme and mode variations. Consider extracting a private helper function to reduce duplication.
+@Composable +private fun ReplyBottomSheetActionsPreviewContent( + darkTheme: Boolean, + mode: ReplyBottomSheetMode +) { + val reply = if (mode == ReplyBottomSheetMode.EDIT) Replies.OnTheRadar.reply1 else null + + ReplyRadarPreviewWrapper(darkTheme = darkTheme) { + ReplyBottomSheetActions( + state = ReplyBottomSheetState( + replyBottomSheetMode = mode, + reply = reply + ), + onArchive = {}, + onResolve = {}, + onDelete = {}, + onSave = {}, + onInvalidReminderValue = {}, + selectedDate = null, + selectedTime = null, + name = reply?.name ?: "", + subject = reply?.subject ?: "" + ) + } +} @Preview(showBackground = true) @Composable -fun ReplyBottomSheetActionsPreview1() { - ReplyRadarPreviewWrapper( - darkTheme = false - ) { - // ... existing content - } -} +fun ReplyBottomSheetActionsPreview1() = + ReplyBottomSheetActionsPreviewContent(false, ReplyBottomSheetMode.CREATE)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyOutlinedButtonPreview.kt (1)
21-175: Consider reducing code duplication with a helper function.Similar to other preview files, this has repetitive structure that could benefit from a shared helper function.
+@Composable +private fun ReplyOutlinedButtonStatePreview( + darkTheme: Boolean, + primaryButton: Pair<String, DrawableResource>, + secondaryButton: Pair<String, DrawableResource> +) { + ReplyRadarPreviewWrapper(darkTheme = darkTheme) { + Row( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface) + .padding(8.dp) + ) { + ReplyOutlinedButton( + text = primaryButton.first, + icon = primaryButton.second, + onClick = {} + ) + ReplyOutlinedButton( + text = secondaryButton.first, + icon = secondaryButton.second, + onClick = {} + ) + } + } +}composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/ReplyListScreenPreview.kt (1)
11-13: Add descriptivenameto @Preview for easier Studio lookupWhen many previews share similar signatures, Android Studio lists only the function names. Supplying
name = "…", e.g.-@Preview(showBackground = true) +@Preview(name = "On-the-Radar • Empty • Light", showBackground = true)makes the picker far more usable.
Also applies to: 31-33, 51-53, 71-73, 91-93, 111-113, 131-133, 151-153, 171-173, 191-193, 211-213, 231-233
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (47)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/Previews.kt(0 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ActivityLogListItemPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/FloatingActionButtonPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/PlatformDatePickerPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/PlatformTimePickerPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesArchivedScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesOnTheRadarScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesResolvedScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetActionsPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetContentPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyButtonPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyConfirmationDialogPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListItemPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyNotificationPermissionDialogPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyOutlinedButtonPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyProgressPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarErrorPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarPlaceholderPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyReminderPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRoundedCornerPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplySnackbarPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTabPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTextFieldPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTimestampInfoPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTogglePreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/TopBarPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/ActivityLogScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/ReplyListScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/SettingsScreenPreview.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/Shared.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/fakes/FakeNotificationPermissionManager.kt(1 hunks)composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/fakes/PreviewStringsFake.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarError.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarPlaceholder.kt(2 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt(3 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt(3 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt(1 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyList.kt(2 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt(2 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt(2 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt(2 hunks)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt(4 hunks)
💤 Files with no reviewable changes (1)
- composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/Previews.kt
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rafaelfelipeac
PR: rafaelfelipeac/replyradar#5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:0-0
Timestamp: 2025-07-24T22:13:32.263Z
Learning: The user rafaelfelipeac consistently implements robust solutions that go beyond the immediate problem, adding defensive programming measures like the isPendingReplyHandled flag in the ReplyListViewModel to prevent duplicate processing, showing strong attention to edge cases and race conditions.
Learnt from: rafaelfelipeac
PR: rafaelfelipeac/replyradar#5
File: config/detekt/detekt.yml:15-17
Timestamp: 2025-07-24T20:32:51.160Z
Learning: The user rafaelfelipeac prefers to create tech debt tasks to track future cleanup and refactoring work when temporarily relaxing code quality thresholds, showing a proactive approach to technical debt management.
📚 Learning: in replyradar app, reply entities have mutually exclusive states: a reply can only be in one state a...
Learnt from: rafaelfelipeac
PR: rafaelfelipeac/replyradar#5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:310-329
Timestamp: 2025-07-24T20:32:50.724Z
Learning: In ReplyRadar app, Reply entities have mutually exclusive states: a reply can only be in one state at a time (pending/resolved/archived). This means replies cannot exist in multiple collections simultaneously, eliminating concerns about race conditions when processing replies across different state-based collections in the ReplyListViewModel.
Applied to files:
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarError.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesOnTheRadarScreenPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyButtonPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRoundedCornerPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTimestampInfoPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesArchivedScreenPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyConfirmationDialogPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesResolvedScreenPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplySnackbarPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyReminderPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarPlaceholderPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListItemPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyProgressPreview.ktcomposeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyList.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTogglePreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetActionsPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTextFieldPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyOutlinedButtonPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/Shared.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarErrorPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetContentPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTabPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/ReplyListScreenPreview.kt
📚 Learning: the user rafaelfelipeac consistently implements robust solutions that go beyond the immediate proble...
Learnt from: rafaelfelipeac
PR: rafaelfelipeac/replyradar#5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:0-0
Timestamp: 2025-07-24T22:13:32.263Z
Learning: The user rafaelfelipeac consistently implements robust solutions that go beyond the immediate problem, adding defensive programming measures like the isPendingReplyHandled flag in the ReplyListViewModel to prevent duplicate processing, showing strong attention to edge cases and race conditions.
Applied to files:
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesResolvedScreenPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListItemPreview.ktcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/Shared.kt
🧬 Code Graph Analysis (11)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarTheme.kt (1)
ReplyRadarTheme(7-20)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesOnTheRadarScreenPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt (1)
RepliesScreen(18-59)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyConfirmationDialogPreview.kt (3)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyConfirmationDialog.kt (1)
ReplyConfirmationDialog(26-95)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/String.kt (1)
format(3-11)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplySnackbarPreview.kt (1)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyReminderPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1)
ReplyReminder(42-165)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyRadarPlaceholderPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarPlaceholder.kt (1)
ReplyRadarPlaceholder(12-22)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyList.kt (1)
ReplyList(21-63)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTextFieldPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTextField.kt (1)
ReplyTextField(29-75)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ActivityLogListItemPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (1)
ActivityLogListItem(209-258)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyBottomSheetContentPreview.kt (2)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
ReplyRadarPreviewWrapper(11-21)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (1)
ReplyBottomSheetContent(31-103)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt (3)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.android.kt (1)
getAppVersion(7-12)composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.kt (1)
getAppVersion(3-3)composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.native.kt (1)
getAppVersion(5-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Build, Tests and Lint
🔇 Additional comments (35)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1)
181-183: Typography updated – looks goodExplicitly setting
color = colorScheme.onSurfacealigns with Material 3 guidance.
No further action needed.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/fakes/FakeNotificationPermissionManager.kt (1)
5-8: Stub implementation is adequate for previewsReturns deterministic truthy result and avoids side-effects – perfect for design-time previews.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarPlaceholder.kt (1)
18-20: Explicit colour on placeholder – looks correctUsing
colorScheme.onSurfacematches the (implicit) surface background in most containers.
No issues here.composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (1)
5-5: LGTM! Explicit theming improves consistency.The addition of explicit text color using
MaterialTheme.colorScheme.onSurfaceensures proper theming in both light and dark modes, which aligns well with the comprehensive preview infrastructure being introduced in this PR.Also applies to: 27-28
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyList.kt (1)
3-3: LGTM! Background theming enhances visual consistency.Adding the surface background color to reply items provides consistent visual styling across the app and aligns with the Material3 design system.
Also applies to: 37-37
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt (1)
16-16: LGTM! Architecture improvement with proper separation of concerns.The refactoring to use
ScreenRootcomposables for navigation properly separates state collection from UI rendering, following good architectural practices. This ensures the navigation layer interacts with state-aware components while keeping UI components pure.Also applies to: 20-20, 55-55, 70-70
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)
3-3: LGTM! Consistent theming implementation.The background color addition follows the established pattern across the PR, ensuring visual consistency in the bottom sheet actions while maintaining all existing functionality.
Also applies to: 11-11, 59-59
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (1)
118-119: LGTM! Fixes initial pager state consistency.Setting
initialPage = state.selectedTabIndexensures the pager starts on the correct tab, preventing potential UI inconsistency where the selected tab and displayed page could be mismatched during initialization.composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt (1)
13-13: LGTM! Excellent improvement for component reusability.Adding a default
Modifierparameter follows Compose best practices and makes the component more convenient to use, especially in preview scenarios where explicit modifiers are often unnecessary.composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt (1)
14-14: LGTM! Good pattern for preview-friendly components.Making the
onIntentparameter optional with a default empty lambda is an excellent approach that maintains existing functionality while enabling the component to be used in previews without requiring real event handling logic.composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarError.kt (2)
13-13: LGTM! Good improvement for component reusability.Making the
errorMessageparameter optional with a sensible default enhances the component's usability, especially in preview scenarios.
15-16: Excellent theming improvements for Material3 consistency.The explicit background and text color usage ensures proper visual contrast and consistency across light/dark themes. Using
MaterialTheme.colorScheme.surfaceandonSurfacefollows Material3 design guidelines perfectly.Also applies to: 20-20
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/ReplyRadarPreviewWrapper.kt (1)
11-21: Excellent shared preview infrastructure design!This wrapper provides a clean, centralized approach to preview setup that:
- Eliminates code duplication across preview files
- Ensures consistent theming and localization context
- Uses appropriate fake implementations for dependencies
- Enables easy light/dark theme testing
The implementation follows Compose best practices and aligns well with your preference for robust, comprehensive solutions.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/TopBarPreview.kt (2)
8-19: Well-structured light theme preview.Clean implementation that leverages the shared preview wrapper and provides appropriate empty handlers for preview scenarios.
21-32: Excellent dual-theme preview approach.Providing both light and dark theme previews ensures comprehensive visual testing. The consistent structure and naming convention make it easy to understand the purpose of each preview.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/PlatformTimePickerPreview.kt (1)
13-23: Nullability Verified forPlatformTimePickerParameters
AllPlatformTimePickerdeclarations incommonMain,androidMain,iosMain, anddesktopMaindefineselectedTime: LocalTime?andselectedDate: LocalDate?. Passingnullin the previews is correct—no changes required.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesOnTheRadarScreenPreview.kt (1)
14-26: LGTM – solid state isolationPreview sets up realistic
ReplyListStatewith sample data and keeps side-effects nil. Good separation of concerns.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/shared/fakes/PreviewStringsFake.kt (1)
55-61: Placeholder count safety.Several strings contain multiple
%placeholders (e.g.%1 at %2). The helper incore.util.formatperforms sequentialreplace, so supplying a mismatching arg count will produce a malformed string at runtime.
A defensive assertion (unit-test orrequire(args.size == expected)) aroundformatwould surface issues early, especially because these strings are duplicated here and in production resources.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/FloatingActionButtonPreview.kt (1)
14-29: LGTM – clean, minimal preview implementation.Nothing blocking; layout and theme wrapper are correctly applied.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesArchivedScreenPreview.kt (2)
11-39: LGTM – consistent with other tab previews.The setup matches the OnTheRadar/Resolved previews, ensuring parity across tabs.
16-23: No action needed –ReplyListStateprovides defaults for all parameters
TheReplyListStateprimary constructor declares default values forreplies,resolvedReplies,archivedReplies,isLoading, anderrorMessage, so supplying onlyisLoadingandarchivedRepliesis valid and will compile.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyConfirmationDialogPreview.kt (1)
11-51: LGTM – previews correctly exercise light & dark variants with formatted strings.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/RepliesResolvedScreenPreview.kt (1)
1-44: LGTM! Well-structured preview implementation.The preview follows excellent patterns:
- Consistent use of shared constants (
RESOLVED_INDEX) and mock data (Replies.Resolved.replies)- Proper theme isolation with light/dark variants
- Appropriate callback stubbing with empty lambdas for preview isolation
- Good use of the shared
ReplyRadarPreviewWrapperfor consistent themingcomposeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyListItemPreview.kt (1)
1-64: LGTM! Comprehensive preview coverage.Excellent preview implementation with:
- Good coverage using different sample data (
reply1,reply2)- Consistent light/dark theme variants
- Clear function naming convention
- Proper callback isolation with empty lambdas
- Consistent use of shared preview infrastructure
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplySnackbarPreview.kt (2)
17-21: Excellent documentation explaining preview limitations.The comment clearly explains why the actual
ReplySnackbarcomponent can't be used directly in previews due toSnackbarHostStaterequiring runtime coroutine execution. This kind of documentation is invaluable for future maintainers.
28-40: LGTM! Correct manual simulation approach.The manual simulation correctly replicates the styling from the actual
ReplySnackbarcomponent:
- Uses matching
containerColorandcontentColorfrom the theme- Proper
Boxwrapper with background for visual context- Consistent with the component's color scheme usage
Also applies to: 49-61
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/screens/ActivityLogScreenPreview.kt (1)
10-72: Excellent preview coverage and structure!The preview functions provide comprehensive coverage of the ActivityLogScreen with:
- Both empty and populated states
- Light and dark theme variants
- Consistent use of ReplyRadarPreviewWrapper
- Clear, descriptive naming conventions
- Proper state-driven approach enabling isolated UI testing
This aligns well with the refactored ActivityLogScreen separation of concerns.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (1)
86-101: Excellent architectural refactoring!The separation of
ActivityLogScreenRoot(state collection) fromActivityLogScreen(UI rendering) follows clean architecture principles:
- Enables isolated UI testing and previewing
- Maintains clear separation of concerns
- Consistent with the broader refactoring pattern in the codebase
- Makes the UI composable purely state-driven
This approach facilitates the comprehensive preview coverage demonstrated in the preview files.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ActivityLogListItemPreview.kt (2)
320-338: Excellent use of helper function to reduce duplication.The
PreviewActionhelper function effectively eliminates code duplication across the numerous preview variants while maintaining flexibility for different parameters. The use ofClock.System.now()with time offsets is a clever approach to demonstrate varied timestamps in previews.
28-166: Comprehensive coverage of user action scenarios.The preview functions provide excellent coverage of all UserActionType and UserActionTargetType combinations, with meaningful test data from the shared Replies object. This will be valuable for visual testing and development.
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyOutlinedButtonPreview.kt (1)
21-175: Good coverage of button states with appropriate icons and text.The previews effectively demonstrate the ReplyOutlinedButton component across different reply states (Active/Resolved/Archived) with contextually appropriate icons and text. The Row layout provides good visual context for how buttons appear together.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt (3)
68-83: Excellent separation of state management from UI rendering.The split into
SettingsScreenRootandSettingsScreenfollows excellent Compose patterns by separating state management concerns from pure UI rendering. This improves testability and reusability of the UI composable.
383-402: Good elimination of side effects in UI composable.Moving the
getAppVersion()call out of theAppVersionFootercomposable and passing it as a parameter eliminates side effects and makes the component more testable and predictable.
130-144: Clean parameter passing pattern for intent handling.The consistent use of
onIntentparameter throughout the component hierarchy (Theme, Language, App components) provides a clean and predictable way to handle user interactions without tight coupling to the ViewModel.composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/previews/components/ReplyTabPreview.kt (1)
14-45: Good use of TabRow with proper indicator positioning.The TabRow setup with
tabIndicatorOffsetand Material3 theming is well-implemented. The OnTheRadar preview correctly shows the first tab as selected, matchingselectedTabIndex = 0.
Summary by CodeRabbit
New Features
Refactor
Style
Chores