Skip to content

Reminder#5

Merged
rafaelfelipeac merged 59 commits intomainfrom
feat/reminder
Jul 28, 2025
Merged

Reminder#5
rafaelfelipeac merged 59 commits intomainfrom
feat/reminder

Conversation

@rafaelfelipeac
Copy link
Owner

@rafaelfelipeac rafaelfelipeac commented Jul 23, 2025

image

Summary by CodeRabbit

  • New Features

    • Added support for scheduling and managing reply reminders with customizable date and time.
    • Introduced notification reminders for replies, including permission handling and in-app dialogs.
    • Added new UI components for reminder selection, notification permission prompts, and improved reply list interactions.
    • Integrated notification channels and permission management for Android 13+.
    • Reply reminders trigger notifications and are visible in the activity log.
  • Enhancements

    • Improved reply list and bottom sheet UI with clearer actions and timestamp info.
    • Expanded localization with new reminder and notification-related strings.
    • Streamlined navigation for notification-driven reply opening.
  • Bug Fixes

    • Fixed default timestamp handling and improved date/time validation.
  • Refactor

    • Simplified and unified intent handling, state management, and effect flows in reply list features.
    • Updated dependency injection and removed obsolete clock utilities.
  • Chores

    • Updated and reorganized imports, constants, and package structures for clarity and maintainability.
    • Added and updated tests for new reminder and notification features.

@coderabbitai
Copy link

coderabbitai bot commented Jul 23, 2025

Walkthrough

This update introduces a comprehensive reminder and notification feature set, including new database schema changes, domain models, UI components, and notification scheduling using WorkManager. Notification permissions are now managed with platform-specific implementations and user prompts. The codebase also underwent significant refactoring, including package restructuring, removal of the Clock abstraction, and updates to dependency injection, theming, and string localization.

Changes

File(s) / Group Change Summary
build.gradle.kts Incremented versionCode and versionName; added Android WorkManager dependency.
AndroidManifest.xml Added POST_NOTIFICATIONS permission.
.../ReplyRadarDatabase/1.json, .../ReplyRadarDatabase/2.json Updated DB schema: added reminderAt column; added user_actions table; updated identity hashes.
.../ReplyRadarDatabase.kt, .../ReplyRadarMigrations.kt, .../DatabaseFactory.kt Bumped DB version to 2; added migration 1->2 for reminderAt; enabled migrations in database builder.
.../ReplyEntity.kt, .../Reply.kt, .../ReplyMapper.kt Added reminderAt property; updated default timestamp values to INITIAL_DATE.
.../ReplyRepositoryImpl.kt, .../UserActionRepositoryImpl.kt Removed Clock dependency; replaced with direct calls to getCurrentTimeMillis(); updated constants usage.
.../AppConstants.kt, .../Int.kt Added new constants and integer extension for two-digit string formatting.
.../Datetime.kt, .../DatetimeExt.kt, .../Timestamp.kt Added new date/time utility functions and formatting helpers.
.../PlatformDatePicker.kt, .../PlatformTimePicker.kt Added expect declarations for multiplatform date/time pickers; Android actual implementations added.
.../NotificationPermissionManager.kt, .../LocalNotificationPermissionManager.kt Added notification permission manager interface and Compose composition local.
.../NotificationUtils.kt, .../ReminderScheduler.kt, .../ReminderSchedulerImpl.kt, ... Added notification display and scheduling utilities; implemented ReminderScheduler for Android using WorkManager.
.../ReplyReminder.kt, .../FormatReminderText.kt, .../ReplySnackbar.kt, ... Added new UI components for reminders, formatted reminder text, and snackbar.
.../ReplyListScreen.kt, .../ReplyListEffect.kt, .../ReplyListScreenIntent.kt, ... Refactored reply list to use effect-based flow; added reminder and notification logic; updated intents and state.
.../ReplyListViewModel.kt, .../ReplyListState.kt, ... Enhanced ViewModel for notification permission flow, reminder scheduling, effect emission, and pending reply handling.
.../ReplyBottomSheet.kt, .../ReplyBottomSheetContent.kt, ... Refactored bottom sheet to support explicit callbacks, reminders, snackbar, and new UI structure.
.../ReplyTimestampInfo.kt, .../ReplyBottomSheetActions.kt, ... Added timestamp info and bottom sheet action composables.
.../ActivityLogScreen.kt, .../UserActionType.kt Added new user action types for scheduling and notification; updated icons and verbs.
.../Strings.kt, .../StringsEn.kt, .../StringsPt.kt, ... Extended string resources for reminders, notifications, permission dialogs, and activity log verbs.
.../theme/ColorScheme.kt, .../theme/ReplyRadarColors.kt, ... Added new theme color for bottom sheet icons; refactored theme package structure.
.../di/Modules.kt, .../di/Modules.android.kt, .../ReplyModule.kt Updated DI modules to add ReminderScheduler and remove Clock singleton.
.../AppNavHost.kt, .../ReplyRadarApp.kt, .../AndroidApp.kt, .../MainActivity.kt Updated navigation and app entry points to support pending reply navigation and notification permission manager.
.../core/external/External.kt, .../core/external/External.android.kt, ... Refactored package structure for external utilities.
.../core/language/AppLanguage.kt, .../core/version/Version.kt, ... Moved language and version files to new packages; added expect for version.
.../core/util/Clock.kt, .../core/util/Timestamp.kt, .../core/util/Version.kt Removed obsolete Clock and timestamp utilities; replaced with new datetime utilities.
.../ReplyListScreenPreview.kt, .../ReplyConfirmationDialog.kt, ... Updated previews and minor UI refactors for new parameters and code style.
.../fakes/core/util/FakeReminderScheduler.kt Added test double for ReminderScheduler.
.../fakes/core/util/FakeClock.kt, .../SnackbarState.kt Removed obsolete test and UI state files.
.../Shared.kt, .../FakeSettingsRepository.kt, ... Minor test code updates for new constants and imports.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant UI
    participant ViewModel
    participant ReminderScheduler
    participant NotificationPermissionManager
    participant WorkManager
    participant NotificationUtils

    User->>UI: Set Reminder on Reply
    UI->>ViewModel: OnAddOrEditReply Intent
    ViewModel->>ViewModel: Validate Reminder Date/Time
    alt Permission Needed
        ViewModel->>NotificationPermissionManager: ensureNotificationPermission()
        NotificationPermissionManager-->>ViewModel: Permission Result
        alt Permission Granted
            ViewModel->>ReminderScheduler: scheduleReminder(reminderAt, params)
            ReminderScheduler->>WorkManager: Enqueue Work
        else Permission Denied
            ViewModel->>UI: Show NotificationPermissionDialog
        end
    else No Permission Needed
        ViewModel->>ReminderScheduler: scheduleReminder(reminderAt, params)
        ReminderScheduler->>WorkManager: Enqueue Work
    end
    WorkManager->>NotificationUtils: showReminderNotification()
    NotificationUtils->>User: Show Notification
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

Oh what a hop, what a leap, what a feat!
Now reminders and toasts make the app complete.
With WorkManager's help and permission in tow,
Notifications pop up, so users will know.
Dates and times sparkle, the UI’s refined—
A bunny’s delight, with deadlines in mind!
🐇⏰✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/reminder

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.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need 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)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@rafaelfelipeac rafaelfelipeac marked this pull request as ready for review July 23, 2025 19:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Nitpick comments (26)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/StatusBar.kt (2)

10-15: Replace manual ARGB math with the built-in Color.toArgb()

Compose already provides Color.toArgb(), avoiding the custom multiplier constant and reducing risk of rounding errors.

-    window.statusBarColor = android.graphics.Color.argb(
-        (color.alpha * COLOR_MULTIPLIER).toInt(),
-        (color.red * COLOR_MULTIPLIER).toInt(),
-        (color.green * COLOR_MULTIPLIER).toInt(),
-        (color.blue * COLOR_MULTIPLIER).toInt()
-    )
+    window.statusBarColor = color.toArgb()

If you apply this, you can drop COLOR_MULTIPLIER.


17-18: Null-safety for WindowInsetsController

WindowCompat.getInsetsController can return null on some OEM skins. Use a safe call to avoid a rare NPE.

-    WindowCompat
-        .getInsetsController(window, window.decorView).isAppearanceLightStatusBars = useDarkIcons
+    WindowCompat.getInsetsController(window, window.decorView)
+        ?.let { it.isAppearanceLightStatusBars = useDarkIcons }
composeApp/build.gradle.kts (1)

218-220: Restrict WorkManager to Android source-set

Adding implementation(libs.androidx.work.runtime.ktx) in the top-level dependencies {} block makes the artifact visible to every target produced by the multiplatform build.
Move it to the existing androidMain.dependencies {} block (or use androidImplementation) to avoid shipping unused Android-only code to other platforms and to prevent future linkage issues.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt (1)

42-43: Nit: modifier split adds no functional value

The line-break re-formatting is harmless, but purely stylistic.
Feel free to keep it if it matches your formatter rules.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/AppConstants.kt (1)

3-21: Well-structured constants organization with room for improvement.

The constants are logically grouped and properly typed. However, consider these improvements:

  1. Email exposure: Hardcoding a personal email address in constants may not be ideal for production code
  2. Magic numbers: Some constants like REMINDER_DEFAULT_HOUR = 8 could benefit from documentation explaining the business logic
  3. Consider grouping: Related constants could be nested in inner objects for better organization
 object AppConstants {
     const val EMPTY = ""
     const val DB_NAME = "replyradar.db"
     const val PACKAGE_NAME = "com.rafaelfelipeac.replyradar"
-    const val EMAIL = "rafaelfelipeac@gmail.com"
+    const val EMAIL = "rafaelfelipeac@gmail.com" // TODO: Move to build config for production
 
     const val PENDING_REPLY_ID_KEY = "PENDING_REPLY_ID"
 
+    object Defaults {
+        const val INITIAL_DATE = 0L
+        const val INITIAL_ID = 0L
+        const val INVALID_ID = -1L
+        const val REMINDER_DEFAULT_HOUR = 8 // 8 AM default reminder time
+        const val REMINDER_DEFAULT_MINUTE = 0
+        const val REMINDER_TOMORROW_OFFSET = 1
+    }
-    const val INITIAL_DATE = 0L
-    const val INITIAL_ID = 0L
-    const val INVALID_ID = -1L
-    const val REMINDER_DEFAULT_HOUR = 8
-    const val REMINDER_DEFAULT_MINUTE = 0
-    const val REMINDER_TOMORROW_OFFSET = 1
 
+    object ReplyStates {
+        const val ON_THE_RADAR_INDEX = 0
+        const val RESOLVED_INDEX = 1
+        const val ARCHIVED_INDEX = 2
+    }
-    const val ON_THE_RADAR_INDEX = 0
-    const val RESOLVED_INDEX = 1
-    const val ARCHIVED_INDEX = 2
 }
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt (1)

27-40: Solid notification channel implementation with minor considerations.

The implementation correctly handles API level checks and uses string resources for internationalization. However, consider these aspects:

  1. High importance: IMPORTANCE_HIGH should be justified - this bypasses DND and makes sound/vibration
  2. Error handling: Consider handling potential SecurityException or other system-level failures
 private fun createNotificationChannel() {
     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
-        val id = getString(notification_channel_id)
-        val name = getString(notification_channel_name)
-        val descriptionText = getString(notification_channel_description)
-        val importance = IMPORTANCE_HIGH
-        val channel = NotificationChannel(id, name, importance).apply {
-            description = descriptionText
-        }
-        val notificationManager = getSystemService(NotificationManager::class.java)
-
-        notificationManager.createNotificationChannel(channel)
+        try {
+            val id = getString(notification_channel_id)
+            val name = getString(notification_channel_name)
+            val descriptionText = getString(notification_channel_description)
+            val importance = IMPORTANCE_HIGH // Consider IMPORTANCE_DEFAULT if high priority isn't required
+            val channel = NotificationChannel(id, name, importance).apply {
+                description = descriptionText
+            }
+            val notificationManager = getSystemService(NotificationManager::class.java)
+            notificationManager.createNotificationChannel(channel)
+        } catch (e: Exception) {
+            // Log error but don't crash the app
+        }
     }
 }
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt (1)

13-26: Consider renaming to avoid naming conflict with Material3 component.

The function name FloatingActionButton conflicts with the imported Material3 component, which could lead to confusion and potential compilation issues.

Consider renaming the function to be more specific:

-fun FloatingActionButton(onIntent: (ReplyListScreenIntent) -> Unit, colorScheme: ColorScheme) {
+fun ReplyListFloatingActionButton(onIntent: (ReplyListScreenIntent) -> Unit, colorScheme: ColorScheme) {

Otherwise, the component implementation is clean with proper Material3 usage and accessibility support.

composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeReminderScheduler.kt (1)

6-21: Add utility methods for test assertions.

The test fake correctly implements the interface but lacks utility methods for verifying interactions. Consider adding methods to access the recorded data for test assertions.

 class FakeReminderScheduler : ReminderScheduler {

     private val scheduledReminders = mutableListOf<Pair<Long, NotificationReminderParams>>()
     private val cancelledReminders = mutableListOf<Long>()

     override fun scheduleReminder(
         reminderAtMillis: Long,
         notificationReminderParams: NotificationReminderParams
     ) {
         scheduledReminders.add(reminderAtMillis to notificationReminderParams)
     }

     override fun cancelReminder(replyId: Long) {
         cancelledReminders.add(replyId)
     }
+
+    fun getScheduledReminders(): List<Pair<Long, NotificationReminderParams>> = scheduledReminders.toList()
+    fun getCancelledReminders(): List<Long> = cancelledReminders.toList()
+    fun wasReminderScheduled(reminderAtMillis: Long): Boolean = 
+        scheduledReminders.any { it.first == reminderAtMillis }
+    fun wasReminderCancelled(replyId: Long): Boolean = cancelledReminders.contains(replyId)
+    fun clear() {
+        scheduledReminders.clear()
+        cancelledReminders.clear()
+    }
 }
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/DatetimeExt.kt (1)

9-11: Consider timezone consistency with the companion function.

The toEpochMillis() function implicitly uses the system timezone, while toLocalDate() allows specifying a timezone parameter. This could lead to inconsistencies if both functions are used together.

Consider making the timezone handling consistent:

-fun LocalDate.toEpochMillis(): Long {
-    return this.atStartOfDayIn(TimeZone.currentSystemDefault()).toEpochMilliseconds()
-}
+fun LocalDate.toEpochMillis(timeZone: TimeZone = TimeZone.currentSystemDefault()): Long {
+    return this.atStartOfDayIn(timeZone).toEpochMilliseconds()
+}
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt (1)

24-39: Simplify the nested layout structure.

The nested Box and Column layout appears unnecessarily complex. The outer Box and inner Box serve similar purposes (centering content), which can be simplified.

Consider this simplified structure:

-    Box(
-        modifier = Modifier
-            .fillMaxSize(),
-        contentAlignment = Alignment.Center
-    ) {
-        Column(
-            modifier = Modifier
-                .fillMaxSize()
-                .background(colorScheme.background),
-            verticalArrangement = Arrangement.Center
-        ) {
-            Box(
-                modifier = Modifier
-                    .fillMaxSize(),
-                contentAlignment = Alignment.Center
-            ) {
+    Column(
+        modifier = Modifier
+            .fillMaxSize()
+            .background(colorScheme.background)
+    ) {
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt (1)

17-18: Consider more intuitive default time values.

The default time of 12:12 seems arbitrary. Consider using more intuitive defaults like 12:00 (noon) or the current time.

-private const val HOUR_DEFAULT_INITIAL_HOUR = 12
-private const val HOUR_DEFAULT_INITIAL_MINUTE = 12
+private const val HOUR_DEFAULT_INITIAL_HOUR = 12
+private const val HOUR_DEFAULT_INITIAL_MINUTE = 0
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderSchedulerImpl.kt (5)

14-18: LGTM! Consider the edge case for delay exactly equal to zero.

The class structure and constant definition look good. The INVALID_DELAY = 0 constant provides clear semantic meaning for invalid delays.

Consider whether a delay of exactly 0 milliseconds should be treated as invalid. If immediate scheduling is a valid use case, you might want to use a negative value or rename the constant to better reflect its purpose.


35-54: LGTM! Well-structured WorkRequest creation.

The method properly configures the WorkRequest with delay, input data, and tagging. The use of string resources for keys ensures consistency across the codebase.

Consider extracting the input data creation to improve readability:

 private fun enqueueReminder(
     delay: Long,
     notificationReminderParams: NotificationReminderParams
 ) {
     with(notificationReminderParams) {
+        val inputData = workDataOf(
+            context.getString(notification_reminder_reply_id) to replyId,
+            context.getString(notification_reminder_title) to notificationTitle,
+            context.getString(notification_reminder_content) to notificationContent
+        )
+        
         val workRequest = OneTimeWorkRequestBuilder<ReminderWorker>()
             .setInitialDelay(delay, MILLISECONDS)
-            .setInputData(
-                workDataOf(
-                    context.getString(notification_reminder_reply_id) to replyId,
-                    context.getString(notification_reminder_title) to notificationTitle,
-                    context.getString(notification_reminder_content) to notificationContent
-                )
-            )
+            .setInputData(inputData)
             .addTag(getTag(replyId))
             .build()

24-26: Consider race condition with system time changes.

The delay calculation using System.currentTimeMillis() could be affected by system time changes. Consider using SystemClock.elapsedRealtime() for more reliable scheduling.

-private fun getDelay(reminderAtMillis: Long) = reminderAtMillis - System.currentTimeMillis()
+private fun getDelay(reminderAtMillis: Long): Long {
+    // Use elapsed realtime to avoid issues with system time changes
+    val currentTimeMillis = System.currentTimeMillis()
+    return reminderAtMillis - currentTimeMillis
+}

44-46: Optimize string resource access.

Accessing string resources multiple times in a loop context could impact performance. Consider caching these keys as constants.

+private companion object {
+    private const val KEY_REPLY_ID = "notification_reminder_reply_id"
+    private const val KEY_TITLE = "notification_reminder_title" 
+    private const val KEY_CONTENT = "notification_reminder_content"
+}

-context.getString(notification_reminder_reply_id) to replyId,
-context.getString(notification_reminder_title) to notificationTitle,
-context.getString(notification_reminder_content) to notificationContent
+KEY_REPLY_ID to replyId,
+KEY_TITLE to notificationTitle,
+KEY_CONTENT to notificationContent

58-58: Potential performance concern with string resource access.

The getTag method accesses string resources which could impact performance when called frequently during cancellation operations.

Consider caching the tag template:

+private companion object {
+    private const val TAG_TEMPLATE = "reminder_%d"
+}

-private fun getTag(replyId: Long) = context.getString(notification_reminder_tag, replyId)
+private fun getTag(replyId: Long) = String.format(TAG_TEMPLATE, replyId)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt (3)

23-46: LGTM! Well-implemented notification display with proper permission checking.

The notification is correctly configured with appropriate settings for a reminder notification. The permission check prevents crashes on Android 13+.

Consider the potential for hash collisions when using replyId.hashCode() as the notification ID. While unlikely for typical use cases, you might want to use the replyId.toInt() directly if the ID range is suitable, or implement a more robust ID generation strategy:

-NotificationManagerCompat.from(context).notify(replyId.hashCode(), notification)
+NotificationManagerCompat.from(context).notify(replyId.toInt(), notification)

48-63: LGTM! Proper PendingIntent creation with correct flags.

The PendingIntent is correctly configured with:

  • Appropriate activity flags for launching from notification
  • Modern FLAG_IMMUTABLE flag for security
  • FLAG_UPDATE_CURRENT for proper intent updates

Same hash collision consideration applies here for the request code. Consider using replyId.toInt() if the ID range permits, for consistency with the notification ID.


45-45: Consider potential hash collision with replyId.hashCode().

Using replyId.hashCode() for both notification ID and PendingIntent request code could lead to collisions, especially with negative hash values. Consider using the replyId directly if it fits within int range, or implement a more robust ID generation strategy.

-NotificationManagerCompat.from(context).notify(replyId.hashCode(), notification)
+NotificationManagerCompat.from(context).notify(replyId.toInt(), notification)

// And in getPendingIntent:
-replyId.hashCode(),
+replyId.toInt(),

Or implement collision-resistant ID generation:

+private fun generateNotificationId(replyId: Long): Int {
+    return (replyId and 0xFFFFFFFF).toInt()
+}

Also applies to: 59-59

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (1)

34-63: LGTM! Logical timestamp priority with proper formatting.

The timestamp display logic correctly prioritizes the most relevant timestamp:

  1. Archived timestamp (highest priority)
  2. Resolved timestamp
  3. Updated timestamp (only if different from created)
  4. Created timestamp (fallback)

The use of INITIAL_DATE for checking timestamp validity and localized string formatting is appropriate.

Consider the edge case where updatedAt equals createdAt but both are set to meaningful values (not INITIAL_DATE). The current logic would show "Created" instead of "Updated", which might be more accurate as "Created" in this scenario.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1)

56-64: Consider handling state updates more atomically.

When deleting a reminder, the state updates and picker visibility changes could be combined into a single state update to avoid potential race conditions.

 onDeleteClick = {
-    onSelectedTimeChange(null)
-    onSelectedDateChange(null)
-    showTimePicker = false
-    showDatePicker = false
+    // Update all states atomically
+    onSelectedTimeChange(null)
+    onSelectedDateChange(null)
+    // Pickers are already closed when reminder is deleted
 }

Since the pickers are only shown when actively selecting, they should already be closed when the delete action occurs.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1)

13-18: Clarify constant naming and values.

The constant LOCAL_TIME_HOUR_DEFAULT = 24 is confusing since valid hours are 0-23. Consider renaming to HOURS_IN_DAY or similar to clarify its purpose in the modulo operation.

-const val LOCAL_TIME_HOUR_DEFAULT = 24
+const val HOURS_IN_DAY = 24

And update the usage:

-return LocalTime(hour = nextHour % LOCAL_TIME_HOUR_DEFAULT, minute = LOCAL_TIME_MINUTE_DEFAULT)
+return LocalTime(hour = nextHour % HOURS_IN_DAY, minute = LOCAL_TIME_MINUTE_DEFAULT)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (2)

133-164: Consider adding error handling for notification content formatting.

The getNotificationTitle and getNotificationContent functions use the format utility which replaces placeholders. If the string templates don't match the expected number of arguments, this could cause issues.

Consider wrapping the formatting in a try-catch or validating the template format:

 is ScheduleReminder -> {
+    try {
         onIntent(
             OnScheduleReminder(
                 reply = effect.reply,
                 replyId = effect.replyId,
                 notificationTitle = getNotificationTitle(strings, effect),
                 notificationContent = getNotificationContent(strings, effect)
             )
         )
+    } catch (e: Exception) {
+        // Log error and show fallback message
+        snackbarHostState.showSnackbar(strings.notificationScheduleError)
+    }
 }

278-299: Consider simplifying the reminder validation logic for better readability.

The current implementation uses multiple chained operations that make the logic harder to follow.

 private fun onSaveReply(
     reply: Reply,
     onCheckNotificationPermission: (Reply) -> Unit,
     onAddOrEditReply: (Reply) -> Unit
 ) {
     val dateTime = getCurrentDateTime()
-    val reminderAt = reply.reminderAt.takeIf { it != INITIAL_DATE }?.dateTime()
-    val selectedTime = reminderAt?.time
-    val selectedDate = reminderAt?.date
 
-    return when {
-        selectedTime?.let { time ->
-            isDateTimeValid(
-                date = selectedDate,
-                time = time,
-                dateTime = dateTime
-            )
-        } == true -> onCheckNotificationPermission(reply)
+    if (reply.reminderAt == INITIAL_DATE) {
+        onAddOrEditReply(reply)
+        return
+    }
 
-        else -> onAddOrEditReply(reply)
+    val reminderDateTime = reply.reminderAt.dateTime()
+    val isValid = isDateTimeValid(
+        date = reminderDateTime.date,
+        time = reminderDateTime.time,
+        dateTime = dateTime
+    )
+
+    if (isValid) {
+        onCheckNotificationPermission(reply)
+    } else {
+        onAddOrEditReply(reply)
     }
 }
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (2)

97-101: Consider using nullable type for pendingReplyId instead of INVALID_ID.

Using INVALID_ID as a sentinel value is less idiomatic in Kotlin compared to using nullable types.

-    private var pendingReplyId: Long? = INVALID_ID
+    private var pendingReplyId: Long? = null

Then update the check in checkPendingReplyId:

-    if (pendingReplyId != INVALID_ID) {
+    if (pendingReplyId != null) {

310-328: Simplify redundant null checking in checkPendingReplyId.

The pendingReplyId?.let block is redundant since we already know it's not null from the outer condition.

 if (reply != null) {
     onTabSelection()
     onOpenReply(reply)
 
-    pendingReplyId?.let { replyId ->
-        logUserAction(
-            actionType = OpenedNotification,
-            targetId = replyId
-        )
-    }
+    logUserAction(
+        actionType = OpenedNotification,
+        targetId = pendingReplyId!!
+    )
 
     pendingReplyId = null
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2c5e3d8 and eea964f.

⛔ Files ignored due to path filters (4)
  • composeApp/src/commonMain/composeResources/drawable/ic_close.png is excluded by !**/*.png
  • composeApp/src/commonMain/composeResources/drawable/ic_date.png is excluded by !**/*.png
  • composeApp/src/commonMain/composeResources/drawable/ic_notification.png is excluded by !**/*.png
  • composeApp/src/commonMain/composeResources/drawable/ic_time.png is excluded by !**/*.png
📒 Files selected for processing (107)
  • composeApp/build.gradle.kts (1 hunks)
  • composeApp/schemas/com.rafaelfelipeac.replyradar.core.database.ReplyRadarDatabase/1.json (3 hunks)
  • composeApp/schemas/com.rafaelfelipeac.replyradar.core.database.ReplyRadarDatabase/2.json (1 hunks)
  • composeApp/src/androidMain/AndroidManifest.xml (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/AndroidApp.kt (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/MainActivity.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/Previews.kt (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/ConfigureSystemBars.kt (0 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/StatusBar.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/ReplyRadarMigrations.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/DatetimeExt.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.android.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.android.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderSchedulerImpl.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderWorker.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.kt (0 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.android.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/di/Modules.android.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/app/ReplyRadarApp.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/AppConstants.kt (0 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/Dimens.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyConfirmationDialog.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt (2 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/ReplyReminder.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplySnackbar.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTextField.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyToggle.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/util/FormatReminderText.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/database/ReplyRadarDatabase.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/language/AppLanguage.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/LocalNotificationPermissionManager.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderScheduler.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/model/NotificationReminderParams.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/LocalReplyRadarStrings.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/Strings.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsEn.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsProvider.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsPt.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ColorScheme.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColorScheme.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColors.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarTheme.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarThemeAccessors.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/model/AppTheme.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/AppConstants.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.kt (0 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Int.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Timestamp.kt (0 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Version.kt (0 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/di/Modules.kt (0 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (11 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/app/settings/AppSettingsViewModel.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/database/entity/ReplyEntity.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/mapper/ReplyMapper.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt (4 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/di/ReplyModule.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/domain/model/Reply.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (5 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreenIntent.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListState.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (10 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/SnackbarState.kt (0 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/RepliesArchivedScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesOnTheRadarScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesResolvedScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.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 (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheet.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/data/repository/SettingsRepositoryImpl.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/repository/SettingsRepository.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/GetLanguageUseCase.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/GetThemeUseCase.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/SetLanguageUseCase.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/SetThemeUseCase.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsIntent.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt (10 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsState.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsViewModel.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/data/repository/UserActionRepositoryImpl.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/domain/model/UserActionType.kt (3 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/Shared.kt (1 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeClock.kt (0 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeReminderScheduler.kt (1 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/settings/data/FakeSettingsRepository.kt (1 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/settings/domain/FakeGetLanguageUseCase.kt (1 hunks)
⛔ Files not processed due to max files limit (26)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/settings/domain/FakeGetThemeUseCase.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/app/settings/AppSettingsViewModelTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/ReplyRepositoryTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/ReplyListViewModelTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/GetLanguageUseCaseImplTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/GetThemeUseCaseImplTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/SetLanguageUseCaseImplTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/SetThemeUseCaseTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsViewModelTest.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/useractions/data/UserActionRepositoryTest.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/Main.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.desktop.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.desktop.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.desktop.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.desktop.kt
  • composeApp/src/desktopMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.desktop.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/MainViewController.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.ios.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.ios.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.ios.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.ios.kt
  • composeApp/src/iosMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.native.kt
  • composeApp/src/main/res/values/strings.xml
  • gradle/libs.versions.toml
💤 Files with no reviewable changes (9)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/ConfigureSystemBars.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/di/Modules.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Version.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeClock.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Clock.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/SnackbarState.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/AppConstants.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Timestamp.kt
🧰 Additional context used
🧬 Code Graph Analysis (14)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/MainActivity.kt (1)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/AndroidApp.kt (1)
  • AndroidApp (13-28)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/AndroidApp.kt (1)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (1)
  • rememberNotificationPermissionManager (29-83)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/data/repository/UserActionRepositoryImpl.kt (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1)
  • getCurrentTimeMillis (25-27)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1)
  • getCurrentTimeMillis (25-27)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.android.kt (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • getCurrentDateTime (21-23)
  • isDateTimeValid (34-51)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/util/FormatReminderText.kt (2)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • getCurrentDateTime (21-23)
  • getDefaultTime (53-77)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/String.kt (1)
  • format (3-11)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (2)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/String.kt (1)
  • format (3-11)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1)
  • formatTimestamp (16-21)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (6)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyButton.kt (1)
  • ReplyButton (12-25)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • isDateTimeValid (34-51)
  • getCurrentDateTime (21-23)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1)
  • getReminderTimestamp (23-56)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt (1)
  • ReplyOutlinedButton (26-56)
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/core/datetime/PlatformTimePicker.android.kt (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • isTimeValid (79-94)
  • getCurrentDateTime (21-23)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt (3)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesOnTheRadarScreen.kt (1)
  • RepliesOnTheRadarScreen (16-43)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesResolvedScreen.kt (1)
  • RepliesResolvedScreen (12-24)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesArchivedScreen.kt (1)
  • RepliesArchivedScreen (13-26)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (3)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1)
  • ReplyReminder (41-156)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (1)
  • ReplyTimestampInfo (19-32)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)
  • ReplyBottomSheetActions (40-103)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderWorker.kt (1)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt (1)
  • showReminderNotification (23-46)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (7)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (1)
  • onIntent (102-108)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt (1)
  • NotificationPermissionDialog (9-35)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplySnackbar.kt (1)
  • ReplySnackbar (10-22)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt (1)
  • FloatingActionButton (13-26)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt (1)
  • ReplyTab (12-27)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • getCurrentDateTime (21-23)
  • isDateTimeValid (34-51)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/String.kt (1)
  • format (3-11)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheet.kt (2)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplySnackbar.kt (1)
  • ReplySnackbar (10-22)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (1)
  • ReplyBottomSheetContent (31-103)
⏰ 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 (169)
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/StatusBar.kt (1)

1-1: All old core.util.StatusBar imports have been removed

I ran a search for com.rafaelfelipeac.replyradar.core.util.StatusBar and found no lingering imports. The package move to com.rafaelfelipeac.replyradar.core is fully applied—no further changes are needed.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarTheme.kt (1)

1-1: Theme package restructuring confirmed

All referenced theme entities are present under com.rafaelfelipeac.replyradar.core.theme:

  • DarkColorScheme & LightColorScheme in ColorScheme.kt
  • DarkExtraColors & LightExtraColors in ColorScheme.kt
  • LocalReplyRadarColors in ReplyRadarColors.kt

No further changes required—approving the package move.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColorScheme.kt (2)

1-1: Package restructuring is consistent.

The package path change and corresponding import update align properly with the theme refactoring effort.

Also applies to: 6-6


26-27: New color property follows established pattern.

The replyBottomSheetIconColor extension property follows the same composable getter pattern as other color extensions, ensuring consistent theme access across the app.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyList.kt (2)

17-17: Import path updated consistently.

The import update aligns with the theme package refactoring from core.common.ui.theme to core.theme.


32-36: Code clarity improvements.

The explicit parameter naming in itemsIndexed(items = replies, ...) improves readability, and the modifier formatting maintains functionality while enhancing code organization.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.android.kt (1)

1-1: Package restructuring improves organization.

Moving from core.util to core.external better reflects the file's purpose of handling external app integrations (email and Play Store). The Android-specific implementation remains unchanged and continues to follow proper error handling patterns.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.android.kt (1)

1-1: Package restructuring enhances modularity.

Moving from core.util to core.version creates better separation of concerns by grouping version-related functionality. The Android implementation correctly handles version retrieval with proper null-safety fallback.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/LocalReplyRadarStrings.kt (1)

1-7: No stale references to core.common.strings found
I ran the provided search scripts and confirmed there are zero hits for core.common.strings in imports or package declarations. All references have been updated. No further action needed.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyRadarError.kt (1)

7-13: Import updated correctly – no further action
LocalReplyRadarStrings now resolves from the new package. Compiles cleanly and preserves behaviour.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTextField.kt (1)

27-27: Confirmed: textFieldPlaceholderColor accessor is present
The textFieldPlaceholderColor property is declared and exported in the new theme package—no build break will occur.

Defined in:

  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColors.kt (val textFieldPlaceholderColor)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColorScheme.kt (val ColorScheme.textFieldPlaceholderColor)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ColorScheme.kt (default values)

No further action required.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarThemeAccessors.kt (1)

1-9: Namespace realignment acknowledged

The theme accessors class follows the new core.theme namespace. No functional change – looks good.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/SetLanguageUseCase.kt (1)

3-15: No residual core.common.language imports detected

Ran:

rg -n "core\.common\.language" --glob "*.kt"

No matches found across all .kt files—all modules appear migrated successfully.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/GetThemeUseCase.kt (1)

3-3: Import path updated – change is correct and consistent with the new theme module structure
No further issues spotted for this segment.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/language/AppLanguage.kt (1)

1-1: No residual core.common.language imports found
Ran repository-wide scans for core.common.language (including import statements) and confirmed zero matches. The relocation is complete—no broken references detected.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/GetLanguageUseCase.kt (1)

3-3: Import path aligns with refactor – looks good

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/usecase/SetThemeUseCase.kt (1)

3-3: Theme import path updated correctly
No other changes required.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/model/AppTheme.kt (1)

1-1: No outdated AppTheme import paths detected
I ran a search for the old core.common.ui.theme.model.AppTheme import and found no occurrences—callers have been successfully migrated.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt (1)

10-10: Import path update looks correct

The new core.theme package is in line with the refactor; no further action required.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/Dimens.kt (1)

30-30: Consistent dimension naming — LGTM

iconButtonSize fits the existing naming scheme and fills the sizing gap between iconSizeLarge and button dimensions. No issues spotted.

composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/settings/domain/FakeGetLanguageUseCase.kt (1)

3-4: Import relocation adopted correctly

The fake use-case now references the new core.language module. Everything else remains intact.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsState.kt (1)

3-5: Import paths updated — looks fine

The SettingsState data class is unchanged; only the package move is reflected. No further action needed.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListState.kt (1)

13-13: Excellent architectural improvement with effect-based UI handling.

Removing snackbarState in favor of an effect-based system using ReplyListEffect is a clean architectural pattern that better separates UI state from side effects. This approach provides better control over snackbar lifecycle and user interactions.

composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/Shared.kt (2)

7-7: Good practice centralizing test timestamp constants.

Adding the now constant improves test maintainability and consistency by eliminating magic numbers.


25-25: Consistent use of centralized test constant.

Using the now constant instead of a hardcoded literal maintains consistency across test data and makes future updates easier.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/app/settings/AppSettingsViewModel.kt (1)

5-6: ✓ Package reorganization verified

The imports in
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/app/settings/AppSettingsViewModel.kt
now correctly reference:

  • com.rafaelfelipeac.replyradar.core.language.AppLanguage
  • com.rafaelfelipeac.replyradar.core.theme.model.AppTheme

Shell checks confirm both files exist at the new paths and are consistently used across the codebase (22 and 21 occurrences respectively). No further changes required.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/data/repository/SettingsRepositoryImpl.kt (1)

7-8: Consistent package reorganization across the codebase.

The import path updates are consistent with the package restructuring seen in other files, moving from core.common.* to more direct core.* packages. This maintains consistency across the settings data layer.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsViewModel.kt (1)

5-6: No obsolete package references detected

A repository-wide search for core.common.language and core.common.ui.theme.model returned no matches, confirming that all legacy package paths have been removed.

composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/settings/data/FakeSettingsRepository.kt (1)

3-4: Imports updated correctly – no further action
Nothing else changed in this file. Looks good.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt (1)

22-23: Extension property verified: import is valid

The ColorScheme.buttonBorderColor extension is declared in
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColorScheme.kt:8,
so the import com.rafaelfelipeac.replyradar.core.theme.buttonBorderColor will resolve correctly.
No further changes needed.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/external/External.kt (1)

1-1: Package relocation verified – all actual implementations updated

All platform-specific External.*.kt files (Android, Desktop, iOS) now use the updated package directive com.rafaelfelipeac.replyradar.core.external. No further changes are necessary.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/domain/repository/SettingsRepository.kt (1)

3-4: Imports updated correctly – no further action
The interface remains unchanged; imports point to the new packages.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsIntent.kt (1)

3-4: LGTM! Clean package refactoring.

The import path updates align with the broader codebase reorganization from core.common.* to more specific packages like core.language and core.theme.model.

composeApp/src/androidMain/AndroidManifest.xml (1)

5-5: LGTM! Required permission for notification features.

The POST_NOTIFICATIONS permission is correctly added to support the new reminder notification functionality introduced in this PR.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyToggle.kt (2)

26-26: LGTM! Import path updated for package refactoring.

The import path change aligns with the broader codebase reorganization from core.common.strings to core.strings.


75-79: LGTM! Improved parameter ordering and explicit sizing.

The modifier parameter reordering and explicit .size(iconSize) makes the code more readable while maintaining the same functionality.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsProvider.kt (1)

1-6: LGTM! Consistent package refactoring.

The package declaration and import path updates align with the broader codebase reorganization, moving from core.common.* to more specific packages like core.strings and core.language.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/di/ReplyModule.kt (1)

34-35: LGTM! Added reminder scheduler dependency injection.

The addition of reminderScheduler dependency to the ReplyListViewModel correctly supports the new reminder scheduling functionality introduced in this PR.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/version/Version.kt (1)

1-3: LGTM! Clean expected function declaration.

The package restructuring from core.util to core.version improves code organization, and the expected function pattern is correctly implemented for multiplatform support.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/MainActivity.kt (3)

6-7: Good addition of required constants.

The imports for INVALID_ID and PENDING_REPLY_ID_KEY support the notification-driven navigation feature.


14-14: Safe intent extra extraction.

The null-safe extraction of pendingReplyId with a proper fallback to INVALID_ID correctly handles cases where the intent might be null or missing the expected extra.


17-17: Proper parameter passing to AndroidApp.

The pendingReplyId parameter is correctly passed to AndroidApp, enabling notification-driven navigation as confirmed by the relevant code snippet showing AndroidApp accepts a Long? parameter.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt (2)

25-28: Clean function signature update.

The addition of the pendingReplyId parameter properly extends the navigation host to support notification-driven navigation.


44-44: Appropriate parameter threading.

The pendingReplyId is correctly passed only to ReplyListScreenRoot since it's the screen responsible for handling notification-triggered reply opening. Other navigation destinations don't need this parameter.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/data/repository/UserActionRepositoryImpl.kt (3)

3-3: Good refactoring to use centralized datetime utility.

The import change from Clock to getCurrentTimeMillis aligns with the broader refactoring to remove the Clock abstraction and use centralized datetime utilities.


33-33: Direct function call improves code clarity.

Using getCurrentTimeMillis() directly is cleaner than the previous clock.now() approach and maintains the same functionality as confirmed by the datetime utility implementation.


20-20: Clock dependency removed and DI updated accordingly

The Clock parameter has been successfully removed from UserActionRepositoryImpl, and the Koin module no longer provides or injects Clock. The repository is now instantiated with only userActionDao and replyDao, matching the updated constructor.

• UserActionRepositoryImpl constructor no longer declares Clock (now only UserActionDao, ReplyDao).
UserActionModule.kt’s singleOf(::UserActionRepositoryImpl) binding aligns with the new two-parameter signature.
• No remaining references to Clock in DI modules for user actions.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyConfirmationDialog.kt (2)

51-54: Good formatting improvements for better readability.

The multi-line formatting of Spacer modifier parameters enhances code readability and follows common Compose coding conventions.

Also applies to: 62-65, 78-81


68-69: Consistent formatting applied to Row modifier.

The multi-line formatting is consistently applied across all modifier parameters in the file.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ReplyRadarColors.kt (2)

1-1: Package restructuring looks good.

The package name change from core.common.ui.theme to core.theme simplifies the structure and aligns with the broader refactoring mentioned in the summary.


14-16: New color property addition follows good practices.

The replyBottomSheetIconColor property is properly added with consistent naming conventions. The trailing comma on snackbarBackgroundColor improves maintainability for future additions.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesOnTheRadarScreen.kt (3)

6-6: Import path simplification is consistent.

The change from core.common.strings to core.strings aligns with the broader package restructuring seen in the theme files.


11-11: Intent renaming improves clarity.

The change from OnReplyClick to OnOpenReply better describes the intent's purpose and aligns with the comprehensive refactoring mentioned in the summary.

Also applies to: 37-37


34-35: Code formatting enhancement.

The multi-line formatting of Modifier.fillMaxSize() improves readability without changing functionality.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesResolvedScreen.kt (1)

6-6: Consistent refactoring across similar components.

The changes mirror those in RepliesOnTheRadarScreen.kt - import path simplification, intent renaming from OnReplyClick to OnOpenReply, and improved code formatting. This consistency across similar components is excellent for maintainability.

Also applies to: 9-9, 18-21

composeApp/schemas/com.rafaelfelipeac.replyradar.core.database.ReplyRadarDatabase/1.json (2)

5-5: Identity hash properly updated for schema changes.

The identity hash change correctly reflects the database schema modifications, which is essential for Room to detect and handle schema changes properly.

Also applies to: 122-122


9-9: Migration Logic Confirmed for reminderAt Column

  • In ReplyRadarMigrations.kt (MIGRATION_1_2) we add
    ALTER TABLE replies ADD COLUMN reminderAt INTEGER NOT NULL DEFAULT 0
  • This migration is included in ALL_MIGRATIONS and applied via .addMigrations(*ALL_MIGRATIONS) in DatabaseFactory.kt

No additional changes required—migration covers existing data.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt (3)

6-6: Import path change consistent with package restructuring.

The change from core.AppConstants to core.util.AppConstants aligns with the broader package reorganization seen throughout the codebase.


7-7: Migration support properly implemented.

The addition of ALL_MIGRATIONS import and its usage in .addMigrations(*ALL_MIGRATIONS) correctly implements database migration support for the schema changes, ensuring smooth upgrades for existing users.

Also applies to: 19-19


16-19: Explicit generic type parameter improves code clarity.

Adding the explicit <ReplyRadarDatabase> generic type parameter to Room.databaseBuilder enhances type safety and code readability.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/Previews.kt (1)

8-8: LGTM! Preview updated correctly for new ReplyListScreen signature.

The changes properly update the preview to accommodate the new effect parameter and onActivityLogClick callback, using an appropriate empty flow for the preview context.

Also applies to: 28-29

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/di/Modules.android.kt (1)

3-3: LGTM! DI module correctly configured for reminder scheduling.

The singleton binding for ReminderScheduler is properly set up with the Android context injection. The singleton scope is appropriate for a scheduling service.

Also applies to: 8-9, 21-21

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/model/NotificationReminderParams.kt (1)

1-7: LGTM! Well-designed data class for notification parameters.

The NotificationReminderParams data class has a clean structure with appropriate types and clear property names, making it suitable for encapsulating notification reminder data.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/LocalNotificationPermissionManager.kt (1)

1-7: LGTM! Composition local properly implemented.

The LocalNotificationPermissionManager follows standard Compose composition local patterns with appropriate error handling for missing providers and clear naming conventions.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/mapper/ReplyMapper.kt (1)

15-16: LGTM! Consistent bidirectional mapping for reminderAt property.

The addition of reminderAt property mapping is correctly implemented in both directions, maintaining consistency between Reply domain model and ReplyEntity data class.

Also applies to: 28-29

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplySnackbar.kt (1)

10-22: Well-designed reusable snackbar component with good theming integration.

The component follows Compose best practices and properly integrates with Material3 theming. The use of extension properties from the theme system ensures consistency across the app.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/theme/ColorScheme.kt (2)

24-25: Consistent color additions with good naming convention.

The new colors snackbarBackgroundColor and replyBottomSheetIconColor are properly defined for both light and dark themes with appropriate contrast values. The naming follows the established convention and clearly indicates their usage context.

Also applies to: 43-44


1-1: Package restructuring validated
Search across the codebase found no remaining references to com.rafaelfelipeac.replyradar.core.common.ui.theme. All imports have been updated to the new com.rafaelfelipeac.replyradar.core.theme package. No further action is required.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (1)

3-8: Well-designed interface following good practices.

The interface is clean with clear method signatures. Using suspend functions is appropriate for permission requests and navigation actions that may involve async operations. The Boolean return type for ensureNotificationPermission() provides clear success/failure indication.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt (1)

9-35: Clean dialog implementation following Compose best practices.

The dialog component is well-structured with proper use of Material3 AlertDialog, localized strings, and composition-based callbacks. The implementation follows standard patterns for permission request dialogs.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/Int.kt (1)

3-6: Clean utility implementation for consistent formatting.

The extension function is well-implemented with proper constants and clear naming. Using padStart is the correct approach for left-padding with zeros to ensure consistent two-digit formatting.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt (2)

34-52: Import path updates align with package restructuring.

The import path changes reflect the package reorganization mentioned in the PR summary, moving from generic common packages to more specific packages like util, language, strings, theme, etc. This improves code organization.


104-107: Improved modifier chain formatting for better readability.

The formatting changes to modifier chains and parameter positioning improve code readability and consistency. The repositioning of verticalAlignment in Row composables follows a more logical parameter order.

Also applies to: 111-114, 118-121, 156-159, 191-194, 210-213, 228-228, 234-234, 252-253, 256-257, 262-265, 281-281, 283-283, 297-298, 301-302, 307-310, 363-366

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/AndroidApp.kt (1)

14-14: Proper integration of notification permission management.

The changes correctly integrate the notification permission system by:

  • Adding the nullable pendingReplyId parameter for notification-driven navigation
  • Obtaining the platform-specific notification permission manager via rememberNotificationPermissionManager()
  • Passing both parameters to ReplyRadarApp for downstream handling

The integration follows good architectural patterns for platform-specific functionality.

Also applies to: 25-26

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderScheduler.kt (1)

5-13: LGTM! Clean interface design with proper abstraction.

The interface follows good design principles with clear method signatures and appropriate separation of concerns between scheduling and cancellation operations.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/ReplyRadarMigrations.kt (1)

8-20: LGTM! Proper database migration implementation.

The migration correctly adds the reminderAt column with appropriate constraints and default value. The dual migrate method implementation supports different SQLite APIs, and the SQL syntax is correct.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt (2)

3-4: LGTM! Clean refactoring from Clock dependency to utility function.

The replacement of the Clock abstraction with direct utility function calls simplifies the code while maintaining the same functionality.


18-44: LGTM! Consistent time handling refactoring.

The replacement of clock.now() calls with getCurrentTimeMillis() and the updated constant usage maintain the same logical behavior while simplifying the time handling approach.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/database/entity/ReplyEntity.kt (2)

5-5: LGTM! Updated import aligns with refactored constants.

The import change from the old constants location to the new core.util.AppConstants location is consistent with the broader refactoring.


14-19: LGTM! Proper addition of reminderAt field with consistent defaults.

The new reminderAt field is properly integrated with:

  • Appropriate default value using INITIAL_DATE
  • Consistent naming convention
  • Proper Long type for timestamp storage
  • Alignment with database schema changes

All timestamp fields now consistently use the updated INITIAL_DATE constant.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/DatetimeExt.kt (1)

13-17: LGTM! Good timezone handling.

The implementation correctly handles timezone conversion and provides a sensible default parameter.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/domain/model/Reply.kt (2)

3-3: LGTM! Improved import path.

The updated import path for INITIAL_DATE reflects the package restructuring and improves organization.


11-15: LGTM! Good use of named constant and new reminder field.

The replacement of magic number 0 with the INITIAL_DATE constant improves code readability and maintainability. The addition of the reminderAt field properly supports the new reminder scheduling feature.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.kt (1)

7-16: LGTM! Well-designed multiplatform interface.

The expect function signature is well-structured with appropriate parameters for a time picker component. The inclusion of selectedDate alongside selectedTime is logical for validation purposes (e.g., preventing past time selection). The customizable text parameters provide good flexibility for different languages and contexts.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/app/ReplyRadarApp.kt (3)

11-20: LGTM! Clean integration of notification functionality.

The updated imports reflect the package restructuring and properly bring in the required notification and strings functionality.


25-29: LGTM! Appropriate parameter additions for notification features.

The new parameters integrate well with the reminder/notification functionality:

  • notificationPermissionManager enables dependency injection of permission handling
  • pendingReplyId supports navigation from notification taps
  • Both parameters have sensible types and nullability

46-51: LGTM! Proper composition local integration.

The notification permission manager is correctly provided through CompositionLocalProvider, making it available throughout the composition tree. The pendingReplyId is appropriately passed to the navigation host to handle notification-based navigation.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderWorker.kt (2)

12-15: LGTM: Proper Worker class structure

The Worker class follows the standard WorkManager pattern with correct constructor parameters and inheritance.


29-37: LGTM: Proper notification handling and result

The notification utility call and success result are correctly implemented. The function properly delegates to the notification utility which handles permission checks.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt (4)

26-27: LGTM: Clean composable function signature

The function has a clear, descriptive name and appropriate callback parameters for the user interactions.


32-41: LGTM: Well-structured activity log text component

The clickable text implementation is correct with proper alignment, padding, and styling. The use of clickable modifier is appropriate for the interaction.


43-51: LGTM: Centered app title with appropriate styling

The centered title uses proper alignment and typography styling. The font size customization with fontSizeLarge provides good visual hierarchy.


53-67: LGTM: Properly implemented settings icon button

The icon button is correctly implemented with proper alignment, padding, and accessibility support through the content description.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.android.kt (4)

19-29: LGTM: Proper function signature and parameter handling

The actual function correctly implements the expected interface with all required parameters.


32-41: LGTM: Correct date picker state with proper restrictions

The date picker state properly initializes with the selected date and correctly restricts selectable dates to current date and future dates only.


45-95: LGTM: Proper dialog implementation with state management

The dialog implementation correctly handles show/hide state, provides proper confirm and dismiss buttons, and manages the date picker component appropriately.


61-71: No changes needed for null-selectedTime handling

The onTimeInvalidated callback simply clears or resets the selected time (e.g. via onSelectedTimeChange(null) in the consumer). Invoking it when selectedTime is already null is idempotent and has no side-effects beyond ensuring the state stays cleared. The current logic correctly treats “no time chosen” as invalid for a newly picked date without introducing any regressions.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformDatePicker.kt (1)

7-16: LGTM: Well-designed multiplatform interface

The expected function signature is well-designed with:

  • Appropriate nullable types for optional selections
  • Clear callback parameter names and types
  • Cross-platform kotlinx-datetime types
  • Comprehensive parameter set for customization

The interface provides good flexibility for platform-specific implementations while maintaining type safety.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/util/FormatReminderText.kt (5)

13-18: LGTM: Proper null handling

The early return when both date and time are null is correct and prevents unnecessary processing.


20-26: LGTM: Proper setup and helper function calls

The current datetime retrieval, default time calculation, and delegation to helper functions is well-structured.


27-34: LGTM: Clean string formatting logic

The conditional formatting based on available date and time parts is well-implemented, using the template formatting utility appropriately.


36-59: LGTM: Comprehensive date part formatting

The getDatePart function correctly handles:

  • Explicit date selection with proper DD/MM/YYYY formatting
  • Today/Tomorrow logic when only time is selected
  • Null case when neither is applicable

The Today/Tomorrow logic correctly compares the reminder time with current time.


61-69: LGTM: Consistent time formatting

The getTimePart function provides consistent HH:MM formatting using the toTwoDigitString() utility, properly handling both selected and default times.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt (2)

18-23: LGTM! Clean composable function signature.

The function parameters and structure are well-designed for a tab-based UI component that delegates rendering to specific screen implementations.


40-55: LGTM! Proper screen routing logic.

The when expression correctly maps page indices to their corresponding screen components, and all parameters are properly passed through.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/domain/model/UserActionType.kt (2)

11-12: LGTM! New user action types align with reminder feature.

The Scheduled and OpenedNotification action types are appropriately named and follow the existing pattern for tracking user interactions with the reminder and notification system.


41-42: LGTM! String constants follow naming convention.

The new constants SCHEDULED and OPENED_NOTIFICATION are consistently named and properly defined.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt (2)

31-44: LGTM! Proper time picker state initialization and validation.

The time picker state is correctly initialized with provided or default values, and the time validation logic properly prevents selection of past times using the isTimeValid utility function.


46-81: LGTM! Well-structured AlertDialog implementation.

The dialog properly handles user interactions, correctly enables/disables the confirm button based on validation, and appropriately calls all callback functions. The dialog state management ensures proper cleanup.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt (1)

5-19: LGTM! Well-designed effect system structure.

The sealed interface approach properly encapsulates different UI effects, and the nested SnackbarState interface effectively groups related snackbar effects. The effect types align well with the notification and reminder features.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (2)

16-21: LGTM! Clean timestamp formatting implementation.

The function correctly converts milliseconds to a readable date-time format using the extension function and proper zero-padding for minutes.


30-49: I’d like to inspect the full Timestamp.kt implementation to confirm how it converts between epoch and LocalDateTime (and whether it applies a timezone). Could you run:

#!/bin/bash
sed -n '1,200p' composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt
composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderSchedulerImpl.kt (3)

20-29: LGTM! Clean validation and delegation pattern.

The method correctly validates the delay before proceeding with scheduling, preventing unnecessary work requests for past times.


31-33: LGTM! Proper use of WorkManager cancellation by tag.

The implementation correctly uses WorkManager's tag-based cancellation, which is the recommended approach for canceling specific work requests.


56-58: LGTM! Simple and correct helper methods.

Both helper methods are well-implemented:

  • getDelay correctly calculates the time difference
  • getTag uses string resources for consistent tag formatting
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (8)

37-37: LGTM! Proper imports for new user action types and updated package paths.

The new imports correctly add support for the Scheduled and OpenedNotification user action types, along with their corresponding icons. The import path updates align with the package restructuring mentioned in the PR.

Also applies to: 44-44, 46-46, 61-61, 64-64, 81-82


98-98: LGTM! Improved modifier formatting for better readability.

The formatting changes improve code readability by placing modifiers on separate lines, which is a good Compose convention.

Also applies to: 131-132, 142-143, 155-156, 172-175


263-264: LGTM! Correct icon mappings for new user action types.

The new cases properly map the Scheduled and OpenedNotification action types to their appropriate icons (ic_time and ic_notification) within the Message target type, which is logically correct.


302-303: LGTM! Consistent localized verb mappings for new action types.

The new cases correctly provide localized verbs for the Scheduled and OpenedNotification action types, maintaining consistency with the existing pattern.


37-37: LGTM! Import path updates align with package restructuring.

The import path changes from core.common.strings to core.strings, and similar updates for theme and datetime utilities are consistent with the package restructuring mentioned in the PR.

Also applies to: 44-44, 46-46


61-61: LGTM! New user action types properly imported.

The imports for Scheduled, OpenedNotification action types and their corresponding icons (ic_time, ic_notification) are correctly added to support the new reminder and notification features.

Also applies to: 64-64, 81-82


263-264: LGTM! Icon mapping updated for new action types.

The getIconByActionType function correctly maps the new Scheduled and OpenedNotification action types to their respective icons when the target type is Message.


302-303: LGTM! Action verb mapping updated consistently.

The getActionVerb function properly handles the new action types with appropriate localized strings for Scheduled and OpenedNotification.

composeApp/schemas/com.rafaelfelipeac.replyradar.core.database.ReplyRadarDatabase/2.json (2)

8-78: LGTM! Proper database schema update for reminder functionality.

The addition of the reminderAt column to the replies table is correctly implemented:

  • Proper INTEGER NOT NULL constraint for timestamp storage
  • Maintains all existing columns and constraints
  • Primary key configuration remains intact

79-118: LGTM! Well-designed user_actions table schema.

The table schema correctly models user actions with:

  • Appropriate nullable constraints (targetType and targetId can be null)
  • Proper data types for each field
  • Auto-generated primary key
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (3)

19-32: LGTM! Well-structured composable with proper null safety.

The component correctly:

  • Uses ColumnScope extension for proper layout context
  • Implements null-safe access to the reply data
  • Applies appropriate styling and alignment

20-32: LGTM! Well-structured Compose component with proper null handling.

The component correctly uses ColumnScope extension, handles null state gracefully with safe call operator, and follows Material3 design guidelines with appropriate typography and alignment.


35-63: LGTM! Logical timestamp priority implementation.

The getTimestampInfo function implements a clear priority hierarchy for timestamp display:

  1. Archived timestamp (highest priority)
  2. Resolved timestamp
  3. Updated timestamp (if different from created)
  4. Created timestamp (fallback)

The logic correctly uses INITIAL_DATE constant for comparison and properly formats timestamps with localized strings.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsPt.kt (3)

1-3: Package restructuring looks good.

The package change from core.common.strings to core.strings simplifies the package structure, and the import has been correctly updated to match.


45-58: Reminder strings are well-structured and properly localized.

The Portuguese translations are appropriate and the placeholder pattern for date/time formatting is consistent with the format utility function usage.


108-121: Notification and activity log strings are complete and user-friendly.

The Portuguese translations effectively communicate the notification permission requirements and provide clear CTAs. The notification content templates handle both cases with and without subject appropriately.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (3)

32-39: Good refactoring with clear separation of concerns.

The composable now focuses on input management while delegating complex UI logic to specialized components. The explicit callbacks (onSave, onResolve, onArchive, onDelete, onInvalidReminderValue) provide better clarity than a single intent handler.


43-45: Proper state management for reminder date and time.

The extraction correctly filters out initial/default dates and properly converts the timestamp to date and time components. Using remember(reminderAt) ensures the state updates when the reminder changes.


78-100: Clean component composition with proper responsibility delegation.

Each component has a well-defined purpose:

  • ReplyReminder: Manages reminder selection UI
  • ReplyTimestampInfo: Displays timestamps
  • ReplyBottomSheetActions: Handles actions and validation

The keyboard management enhances user experience.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (3)

106-146: Well-structured state-based UI rendering.

The pattern matching on reply states (active, resolved, archived) provides clear separation of UI logic for each state. The use of weight(1f) ensures proper space distribution in the row layout.


192-218: Proper implementation of delete confirmation flow.

The delete dialog correctly manages its visibility state and provides clear messaging to the user by including the reply name in the confirmation message.


223-236: Clean implementation of reply creation/update logic.

The function properly handles both scenarios using Kotlin's copy method for immutability when editing existing replies.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (3)

43-52: Correct handling of Android version compatibility.

The implementation properly returns true for Android versions below TIRAMISU (API 33) where POST_NOTIFICATIONS permission is not required, and correctly checks the permission status for newer versions.


54-68: Sophisticated coroutine implementation for permission handling.

The use of suspendCancellableCoroutine with snapshotFlow effectively bridges the callback-based permission API with suspend functions. The cancellation handling ensures no memory leaks.


71-80: Safe implementation of app settings navigation.

The function safely handles the context cast and creates the proper intent to navigate to the app's settings page.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (3)

73-107: Well-structured platform picker integration.

The conditional rendering and callback handling for both date and time pickers is properly implemented. The onTimeInvalidated callback in the date picker ensures consistency when changing dates.


115-147: Good UX consideration with keyboard management.

The icon buttons properly close the keyboard before showing pickers, preventing UI overlap issues. Accessibility is well-handled with content descriptions.


158-193: Clean implementation of reminder text display.

The conditional rendering and delete functionality are properly implemented with appropriate styling and layout.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheet.kt (3)

24-30: Excellent architectural refactoring from intent-based to callback-based design.

The explicit callbacks (onSave, onResolve, onArchive, onDelete, onDismiss) improve code clarity, testability, and follow Compose best practices by making data flow more explicit and reducing coupling between components.


32-42: Well-implemented snackbar state management.

The LaunchedEffect properly handles the invalid reminder value flag by showing the snackbar message and resetting the flag. The use of remember { SnackbarHostState() } ensures the snackbar state persists across recompositions.


80-80: Good placement of ReplySnackbar component.

Placing the ReplySnackbar inside the ModalBottomSheet ensures snackbar messages are displayed within the context of the bottom sheet, providing better user experience for validation errors.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreenIntent.kt (4)

8-8: Good addition for handling pending reply state.

The OnPendingReplyId intent with optional Long? parameter provides flexibility for managing pending reply scenarios, which aligns well with the reminder scheduling feature.


11-11: Better naming for improved clarity.

Renaming OnReplyClick to OnOpenReply is more descriptive and clearly indicates the intent's purpose.


16-16: Smart consolidation of add/edit intents.

Combining OnAddReply and OnEditReply into OnAddOrEditReply reduces code duplication since both operations likely share similar handling logic in the view model.


23-33: Well-structured notification permission intent interface.

The NotificationPermissionIntent sealed interface is well-designed with clear separation of concerns:

  • Permission management (OnRequestNotificationPermission, OnCheckNotificationPermission)
  • Navigation (OnGoToSettings)
  • Reminder scheduling (OnScheduleReminder with comprehensive parameters)

The OnScheduleReminder intent includes all necessary parameters for notification scheduling.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/Strings.kt (4)

1-1: Clean package restructuring.

Moving from core.common.strings to core.strings simplifies the package structure and improves organization.


40-53: Comprehensive reminder string properties.

The reminder-related strings cover all necessary UI elements including labels, validation messages, icon descriptions, and picker button texts. The naming follows consistent patterns with the existing codebase.


89-90: Appropriate activity log verbs for new user actions.

The activityLogUserActionScheduledVerb and activityLogUserActionOpenedNotificationVerb strings properly support the new user action types for reminder scheduling and notification interaction tracking.


96-102: Well-designed notification permission and content strings.

The notification permission dialog strings provide clear messaging, and the notification content templates with placeholders (%1, %2) offer flexibility for different notification scenarios (with and without subject).

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsEn.kt (4)

1-3: Proper package and import updates.

The package change to core.strings and import update to core.version.getAppVersion align with the overall package restructuring.


45-58: Natural and user-friendly reminder translations.

The English translations are clear and conversational. Particularly good choices:

  • "today"/"tomorrow" for relative dates
  • "The selected date and time has already passed." for clear validation feedback
  • Simple "OK"/"Cancel" for picker buttons

108-109: Appropriate English verbs for activity logging.

The activity log verbs "scheduled a reminder for" and "opened a notification for" are grammatically correct and clearly describe the user actions.


115-121: Well-crafted notification permission and content text.

The notification permission dialog text is informative and user-friendly without being pushy. The notification content templates use natural language that will read well when placeholders are filled.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)

19-32: Solid datetime utility functions.

The basic datetime utilities (now(), getCurrentDateTime(), getCurrentTimeMillis(), and the Long.dateTime() extension) provide a clean interface for common datetime operations and properly replace the previous Clock abstraction.


53-77: Complex but functional default time logic.

The getDefaultTime function handles multiple scenarios for determining an appropriate default time. The logic appears correct for validating selected time, falling back to a default time, and calculating next available hour when needed.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (2)

79-94: LGTM! Good implementation of pending reply handling.

The LaunchedEffect properly handles the pending reply ID from notifications, ensuring it's processed when the composable is first composed or when the ID changes.


301-334: Well-structured helper functions for message generation.

The functions properly handle different snackbar states and notification content variations based on whether a subject is present.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (2)

229-238: Good implementation of upsert with reminder scheduling.

The function correctly determines the action type and schedules the reminder after the reply is saved, ensuring a valid reply ID is available.


284-300: Clean implementation of reminder scheduling.

Good use of with() scope function and proper separation of concerns between scheduling the reminder and logging the user action.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesArchivedScreen.kt (3)

6-6: LGTM! Import path updated correctly.

The import path change from core.common.strings to core.strings aligns with the package restructuring mentioned in the PR summary.


9-9: LGTM! Intent import updated consistently.

The import change from OnReplyClick to OnOpenReply is consistent with the intent renaming refactoring across the reply list components.


19-23: LGTM! Formatting and intent usage updated correctly.

The modifier chain formatting improves readability, and the intent usage change from OnReplyClick to OnOpenReply maintains consistency with the refactoring effort.

composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt (2)

41-43: LGTM! Proper permission checking implemented.

The permission check for POST_NOTIFICATIONS is correctly implemented with early return when permission is not granted, preventing SecurityException on Android 13+.


52-55: LGTM! Proper intent configuration for notification tap.

The intent configuration with FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK ensures the app launches cleanly from the notification, and the reply ID is properly passed as an extra.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

♻️ Duplicate comments (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)

78-82: Fix reminder validation logic to prevent potential null pointer issues.

The validation logic has the same issue identified in a previous review. The current implementation only checks if selectedTime is not null before calling isDateTimeValid, but this could cause issues if the function expects non-null parameters.

Apply this fix to ensure safe validation:

-                val reminderIsValid = selectedTime != null && isDateTimeValid(
-                    selectedDate,
-                    selectedTime,
-                    getCurrentDateTime()
-                )
+                val reminderIsValid = when {
+                    selectedDate != null && selectedTime != null -> isDateTimeValid(selectedDate, selectedTime, getCurrentDateTime())
+                    selectedDate == null && selectedTime == null -> true // No reminder selected
+                    else -> false // Incomplete reminder data
+                }
🧹 Nitpick comments (2)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (2)

12-12: Unnecessary import for ExperimentalMaterial3Api

The ExperimentalMaterial3Api import is present but the annotation @OptIn(ExperimentalMaterial3Api::class) is only used for the ReplyListScreen composable. Consider removing this import if it's not needed elsewhere, or ensure it's properly used.


280-301: Potential edge case in date validation logic

The onSaveReply function has a complex validation flow that could benefit from clearer error handling. The nested conditional with takeIf and nullable operations might be hard to debug if issues arise.

Consider simplifying the logic for better readability:

private fun onSaveReply(
    reply: Reply,
    onCheckNotificationPermission: (Reply) -> Unit,
    onAddOrEditReply: (Reply) -> Unit
) {
-    val dateTime = getCurrentDateTime()
-    val reminderAt = reply.reminderAt.takeIf { it != INITIAL_DATE }?.dateTime()
-    val selectedTime = reminderAt?.time
-    val selectedDate = reminderAt?.date
-
-    return when {
-        selectedTime?.let { time ->
-            isDateTimeValid(
-                date = selectedDate,
-                time = time,
-                dateTime = dateTime
-            )
-        } == true -> onCheckNotificationPermission(reply)
-
-        else -> onAddOrEditReply(reply)
-    }
+    if (reply.reminderAt == INITIAL_DATE) {
+        onAddOrEditReply(reply)
+        return
+    }
+    
+    val reminderDateTime = reply.reminderAt.dateTime()
+    val currentDateTime = getCurrentDateTime()
+    
+    if (isDateTimeValid(reminderDateTime.date, reminderDateTime.time, currentDateTime)) {
+        onCheckNotificationPermission(reply)
+    } else {
+        onAddOrEditReply(reply)
+    }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eea964f and a8e2e9d.

📒 Files selected for processing (32)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/ReplyRadarMigrations.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/app/ReplyRadarApp.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyToggle.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/util/FormatReminderText.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsEn.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsPt.kt (3 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (11 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt (4 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (5 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (10 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/RepliesArchivedScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesOnTheRadarScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesResolvedScreen.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (2 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt (10 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/ReplyRepositoryTest.kt (9 hunks)
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/ReplyListViewModelTest.kt (7 hunks)
  • config/detekt/detekt.yml (3 hunks)
✅ Files skipped from review due to trivial changes (4)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesOnTheRadarScreen.kt
  • composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/ReplyRepositoryTest.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt
🚧 Files skipped from review as they are similar to previous changes (21)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesResolvedScreen.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesArchivedScreen.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/settings/presentation/SettingsScreen.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/ReplyRadarMigrations.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/navigation/AppNavHost.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyToggle.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsPt.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/app/ReplyRadarApp.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/util/FormatReminderText.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/TopBar.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/database/DatabaseFactory.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/notification/NotificationPermissionManager.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt
🧰 Additional context used
🧬 Code Graph Analysis (2)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (3)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt (1)
  • ReplyReminder (41-163)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt (1)
  • ReplyTimestampInfo (18-29)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)
  • ReplyBottomSheetActions (40-107)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (7)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (1)
  • onIntent (102-108)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyNotificationPermissionDialog.kt (1)
  • NotificationPermissionDialog (9-32)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplySnackbar.kt (1)
  • ReplySnackbar (10-22)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt (1)
  • FloatingActionButton (13-26)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyTab.kt (1)
  • ReplyTab (12-27)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • getCurrentDateTime (21-23)
  • isDateTimeValid (34-51)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/String.kt (1)
  • format (3-11)
⏰ 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 (22)
composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/ReplyListViewModelTest.kt (4)

64-64: Good addition of reminder scheduler dependency.

The integration of FakeReminderScheduler properly supports testing the new reminder functionality.

Also applies to: 73-73


101-101: Intent renaming improves clarity.

The rename from OnReplyClick to OnOpenReply better describes the action's purpose.


127-138: Well-structured tests for reminder functionality.

The test properly validates that:

  • No reminder is scheduled when reminderAt is INITIAL_DATE
  • ScheduleReminder effect is emitted when a valid reminder timestamp is provided
  • The effect contains the correct reply data

The separation of concerns between state and effect testing is excellent.

Also applies to: 140-154


129-129: Intent unification simplifies the API.

Consolidating separate add and edit intents into OnAddOrEditReply reduces complexity while maintaining functionality.

Also applies to: 159-159

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetContent.kt (3)

34-38: Improved callback interface design.

The rename from onComplete to onSave is more semantically clear, and the addition of onInvalidReminderValue provides better error handling separation.


43-45: Clean state extraction from reminder timestamp.

The conversion of reminderAt to separate selectedDate and selectedTime state variables is well-implemented, with proper null handling and dependency on the reminder value.


78-100: Excellent modularization of UI components.

Breaking down the bottom sheet into focused components (ReplyReminder, ReplyTimestampInfo, ReplyBottomSheetActions) improves maintainability and separation of concerns.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (3)

40-107: Well-designed component structure for bottom sheet actions.

The component properly:

  • Handles conditional UI based on reply state and mode
  • Validates reminder inputs before saving
  • Provides clear callback separation for different actions
  • Manages button states and layouts appropriately

152-223: Excellent state-specific button management.

The state button components (ActiveStateButtons, ResolvedStateButtons, ArchivedStateButton) provide appropriate actions for each reply state, with proper confirmation dialog for destructive actions.


227-240: Clean helper function for reply creation.

The getReplyToSave function properly handles both edit and create scenarios, correctly setting the reminder timestamp in both cases.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/strings/StringsEn.kt (4)

1-1: Good package restructuring and lint management.

The move to core.strings package and updated import paths align with the broader codebase reorganization. The MaxLineLength suppression is appropriate for string definitions.

Also applies to: 3-3, 5-5


48-61: Comprehensive reminder-related strings.

The reminder strings cover all necessary UI elements:

  • Labels and status messages
  • Icon content descriptions for accessibility
  • Error messages for validation
  • Picker button texts

111-112: New activity log verbs support reminder tracking.

The addition of "scheduled a reminder for" and "opened a notification for" verbs properly extends the activity logging system for the new functionality.


118-124: Well-structured notification permission and content strings.

The notification permission dialog strings provide clear user communication, and the notification content templates use proper placeholder formatting for dynamic data.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt (4)

133-166: Excellent effect handling implementation

The side effect collection using LaunchedEffect(Unit) with proper flow collection is well-implemented. The when expression covers all effect types appropriately, and the notification permission flow is handled correctly.


168-176: Good UI state management for permission dialog

The notification permission dialog implementation correctly uses local state management with proper dismiss and navigation handling.


311-328: String formatting functions look good

The notification title and content formatting functions properly handle the optional subject field and use the format utility function correctly.


82-93: pendingReplyId handling validated

  • LaunchedEffect keyed on pendingReplyId only runs on initial composition or when that key changes
  • The ViewModel’s internal pendingReplyId is cleared after processing, preventing duplicate intents
    No further changes are needed.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (4)

97-99: Effect flow implementation is well-designed

The introduction of MutableSharedFlow<ReplyListEffect> for one-time UI effects is a good architectural decision, separating side effects from state management.


148-156: New notification permission intent handling looks good

The notification permission intent handling is well-structured and follows the existing pattern. Each intent is properly delegated to dedicated methods.


229-238: Reminder scheduling logic is well-integrated

The upsert reply logic correctly checks for reminder dates and emits the appropriate effect. The action type inference based on reply ID is clean.


284-300: Reminder scheduling implementation is comprehensive

The onScheduleReminder method properly uses the reminder scheduler and logs user actions. The parameter mapping is clear and follows good practices.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)

77-87: Reminder validation logic is correctly implemented.

The validation logic properly delegates null handling to isDateTimeValid(), which according to the utility function handles all combinations of null/non-null date and time parameters appropriately. The current implementation is safe and follows the expected pattern.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9059fb6 and 1e8b7af.

📒 Files selected for processing (13)
  • CHANGELOG.md (1 hunks)
  • README.md (2 hunks)
  • composeApp/build.gradle.kts (2 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt (1 hunks)
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderWorker.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt (11 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt (4 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (10 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1 hunks)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/domain/model/UserActionType.kt (3 hunks)
  • config/detekt/detekt.yml (3 hunks)
✅ Files skipped from review due to trivial changes (2)
  • CHANGELOG.md
  • README.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • composeApp/build.gradle.kts
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderWorker.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/data/repository/ReplyRepositoryImpl.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/activitylog/presentation/ActivityLogScreen.kt
  • composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/useractions/domain/model/UserActionType.kt
  • config/detekt/detekt.yml
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: rafaelfelipeac
PR: rafaelfelipeac/replyradar#5
File: config/detekt/detekt.yml:15-17
Timestamp: 2025-07-24T20:32:51.112Z
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.
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.250Z
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.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (3)

Learnt from: rafaelfelipeac
PR: #5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:310-329
Timestamp: 2025-07-24T20:32:50.673Z
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.

Learnt from: rafaelfelipeac
PR: #5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:0-0
Timestamp: 2025-07-24T22:13:32.250Z
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: #5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt:21-21
Timestamp: 2025-07-24T20:10:20.763Z
Learning: In ReplyListEffect.ScheduleReminder, the replyId parameter is necessary because it represents the actual database ID returned from upsertReply operation, which differs from reply.id during new reply creation where the reply object initially has a placeholder/default ID.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (1)

Learnt from: rafaelfelipeac
PR: #5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt:310-329
Timestamp: 2025-07-24T20:32:50.673Z
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.

🧬 Code Graph Analysis (1)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (6)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyButton.kt (1)
  • ReplyButton (12-25)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (2)
  • isDateTimeValid (34-56)
  • getCurrentDateTime (21-23)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Timestamp.kt (1)
  • getReminderTimestamp (37-70)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt (1)
  • ReplyOutlinedButton (26-56)
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)
⏰ 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 (10)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/replybottomsheet/ReplyBottomSheetActions.kt (3)

116-149: Well-structured state-dependent button rendering.

The conditional logic correctly handles the mutually exclusive reply states (active/resolved/archived) and delegates to appropriate specialized composables. The use of weight(WEIGHT) ensures proper layout distribution in edit mode.


190-223: Excellent confirmation dialog implementation for destructive actions.

The delete confirmation dialog properly uses local state management and the format utility to create a personalized confirmation message. This follows UI best practices for destructive operations.


227-240: Clean utility functions with proper conditional logic.

The getReplyToSave function elegantly handles both create and update scenarios using the elvis operator, and properly includes the reminder timestamp functionality. The implementation is concise and readable.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt (3)

13-18: Clear and well-named constants for time calculations.

The constant definitions provide good semantic meaning for time-related calculations. The naming convention is consistent and descriptive.


34-56: Comprehensive date/time validation logic.

The validation function correctly handles all combinations of null/non-null date and time parameters, ensuring reminders are only set for future times. The logic is clear and covers all necessary cases.


58-82: Well-designed fallback strategy for default time selection.

The function implements a logical hierarchy: preferred user time → default app time → next available hour. The modulo operation correctly handles 24-hour wrap-around for edge cases.

composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (4)

97-101: Well-implemented effect-based architecture with defensive programming.

The introduction of MutableSharedFlow for effects provides a clean separation between state and one-time events. The isPendingReplyHandled flag demonstrates good defensive programming to prevent duplicate processing, which aligns with your robust implementation approach.


156-166: Excellent solution for race condition prevention.

The handlePendingReplyIdOnce implementation perfectly addresses the previous race condition concerns by immediately processing the pending reply ID with current state while preventing duplicate handling through the defensive flag. This demonstrates strong attention to edge cases.


295-311: Clean integration of reminder scheduling with proper logging.

The onScheduleReminder method effectively integrates the ReminderScheduler and maintains consistent user action logging. The pattern of extracting parameters and scheduling the reminder is clear and follows established conventions.


240-249: Excellent unification of add/edit reply logic.

The unified onUpsertReply method elegantly handles both create and edit scenarios with clean action type inference. The integration of reminder scheduling through effects maintains separation of concerns while reducing code duplication.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1e8b7af and b1087e3.

📒 Files selected for processing (1)
  • composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (10 hunks)
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
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.
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.
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (3)

Learnt from: rafaelfelipeac
PR: #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.

Learnt from: rafaelfelipeac
PR: #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: #5
File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListEffect.kt:21-21
Timestamp: 2025-07-24T20:10:20.784Z
Learning: In ReplyListEffect.ScheduleReminder, the replyId parameter is necessary because it represents the actual database ID returned from upsertReply operation, which differs from reply.id during new reply creation where the reply object initially has a placeholder/default ID.

⏰ 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 (11)
composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt (11)

80-80: LGTM! ReminderScheduler dependency added correctly.

The addition of ReminderScheduler follows proper dependency injection patterns and is necessary for the new reminder scheduling functionality.


97-101: Excellent implementation of effect handling and defensive programming.

The new _effect flow properly handles one-time UI effects, and the pendingReplyId improvements address previous inconsistencies:

  • Nullable initialization to null is semantically correct
  • isPendingReplyHandled flag prevents duplicate processing (great defensive programming!)

147-154: LGTM! Notification permission intent handling follows established patterns.

The method structure is consistent with existing intent handlers and properly delegates to specific handler methods.


156-166: Excellent solution for pending reply handling with race condition prevention.

This method demonstrates strong defensive programming:

  • Immediate processing eliminates race conditions
  • isPendingReplyHandled flag prevents duplicate processing
  • Clean separation of concerns with checkPendingReplyId

This is a more robust solution than simple immediate processing, showing attention to edge cases.


168-177: Good refactoring for reply opening logic.

Extracting this logic into a dedicated method improves code organization and reusability.


193-196: LGTM! Maintains flow-based pending reply checking.

The addition of checkPendingReplyId calls in data flows works well with the immediate processing in handlePendingReplyIdOnce, ensuring comprehensive coverage.


240-249: Excellent unification of add/edit functionality with smart reminder handling.

Key improvements:

  • Smart action type inference based on reply.id == INITIAL_ID
  • Conditional reminder scheduling only when reminderAt != INITIAL_DATE
  • Proper effect emission for UI handling

The logic is clean and handles both creation and editing scenarios effectively.


256-256: Good architectural improvement using effect-based snackbar handling.

Replacing state updates with effect emissions for snackbars provides better separation of concerns and one-time UI events handling.


279-289: LGTM! Clean notification permission effect methods.

These methods properly emit notification-related effects following the established pattern. Simple and effective implementation.


295-311: Comprehensive reminder scheduling implementation.

The method correctly:

  • Schedules reminders using the injected ReminderScheduler
  • Creates proper NotificationReminderParams
  • Logs the scheduling action for user activity tracking
  • Uses the correct replyId from the intent parameter

342-346: Good extraction of tab selection logic.

Clean separation of concerns with a dedicated method for tab selection state updates.

@rafaelfelipeac
Copy link
Owner Author

@coderabbitai, can you organize all the still valid nitpick comments in a list?

@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

I'll help you organize the still valid nitpick comments. Let me gather the existing review comments from this PR first.

@rafaelfelipeac, I searched for existing review comments on this PR, but it appears there are no previous review comments to organize. The GitHub API calls returned empty results, indicating that either:

  1. No review comments have been made yet on this PR
  2. All previous comments have been resolved/addressed
  3. The PR is in a clean state without outstanding nitpick comments

Since there are no existing nitpick comments to organize, there's nothing to list at this time. If you'd like me to perform a fresh review of the code changes and identify potential nitpick items, I'd be happy to do that instead.

Would you like me to:

  1. Perform a new review focusing on code style and minor improvements?
  2. Wait for review comments to be added first?
  3. Something else?

🐰


🏁 Script executed:

#!/bin/bash
# Get all review comments from PR #5
gh pr view 5 --json reviews --jq '.reviews[] | select(.state == "COMMENTED" or .state == "CHANGES_REQUESTED") | {author: .author.login, body: .body, state: .state}'

Length of output: 160431


🏁 Script executed:

#!/bin/bash
# Get review comments with more detail including file context
gh api "repos/rafaelfelipeac/replyradar/pulls/5/reviews" --jq '.[] | select(.state == "COMMENTED" or .state == "CHANGES_REQUESTED") | {id: .id, author: .user.login, body: .body, state: .state}'

Length of output: 153233


🏁 Script executed:

#!/bin/bash
# Get individual review comments (inline comments on code)
gh api "repos/rafaelfelipeac/replyradar/pulls/5/comments" --jq '.[] | {id: .id, author: .user.login, body: .body, path: .path, line: .line, original_line: .original_line}'

Length of output: 68322


@rafaelfelipeac Here is an organized list of all still valid nitpick comments from this PR:


1. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/StatusBar.kt

  • Replace manual ARGB math with Color.toArgb()
  • Null-safety for WindowInsetsController

2. composeApp/build.gradle.kts

  • Restrict WorkManager dependency to Android source-set

3. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyOutlinedButton.kt

  • Modifier split is purely stylistic

4. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/AppConstants.kt

  • Email exposure: Hardcoding a personal email address in constants may not be ideal for production code
  • Magic numbers: Some constants could benefit from documentation
  • Consider grouping: Related constants could be nested in inner objects

5. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt

  • Justify IMPORTANCE_HIGH for notification channel
  • Add error handling for notification channel creation

6. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt

  • Consider renaming to avoid naming conflict with Material3 component

7. composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeReminderScheduler.kt

  • Add utility methods for test assertions

8. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/DatetimeExt.kt

  • Consider timezone consistency in extension functions

9. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt

  • Simplify nested layout structure

10. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt

  • Use more intuitive default time values

11. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderSchedulerImpl.kt

  • Edge case for delay == 0
  • Extract input data creation
  • Use elapsedRealtime for delay
  • Cache string resource keys
  • Cache tag template

12. composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt

  • Hash collision risk with replyId.hashCode()

13. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt

  • Edge case: updatedAt == createdAt but both meaningful

14. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt

  • Handle state updates atomically on delete

15. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt

  • Clarify constant naming for LOCAL_TIME_HOUR_DEFAULT

16. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt

  • Add error handling for notification content formatting
  • Simplify reminder validation logic

17. composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt

  • Use nullable for pendingReplyId instead of INVALID_ID
  • Simplify redundant null checking in checkPendingReplyId

Let me know if you want a more detailed breakdown or if you'd like to mark any of these as intentionally deferred or already addressed!


🧠 Learnings used
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.

@rafaelfelipeac
Copy link
Owner Author

@coderabbitai, can you organize all the still valid nitpick comments in a list? Add details, examples and discussion, when needed. I will use this list in a future PR to attack the nitpick points.

@coderabbitai
Copy link

coderabbitai bot commented Jul 28, 2025

Tip

For best results, initiate chat on the files or code changes.

@rafaelfelipeac Here's a comprehensive, detailed list of all still valid nitpick comments for your future PR:


🔧 Detailed Nitpick Comments List

1. StatusBar.kt - Color & Safety Improvements

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/StatusBar.kt

Issues:

  • Manual ARGB math instead of Color.toArgb()
  • Null-safety for WindowInsetsController

Details & Examples:

// Current approach (manual ARGB calculation)
val argb = (alpha * 255.0f + 0.5f).toInt() shl 24 or
    (red * 255.0f + 0.5f).toInt() shl 16 or
    (green * 255.0f + 0.5f).toInt() shl 8 or
    (blue * 255.0f + 0.5f).toInt()

// Preferred approach
val argb = color.toArgb()

// Null-safety improvement needed
activity?.window?.insetsController?.let { controller ->
    // Safe operations here
}

Why it matters: Manual ARGB calculations are error-prone and less readable. Null-safety prevents potential crashes.


2. Build Configuration - WorkManager Scope

File: composeApp/build.gradle.kts

Issue: WorkManager dependency should be restricted to Android source-set

Details: Currently WorkManager is added to common dependencies, but it's Android-specific.

Example Fix:

// Move from commonMain dependencies to:
androidMain.dependencies {
    implementation(libs.androidx.work.runtime.ktx)
}

Why it matters: Prevents build issues on other platforms and clarifies platform-specific dependencies.


3. AppConstants.kt - Production Readiness

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/util/AppConstants.kt

Issues:

  • Email exposure: Hardcoded personal email
  • Magic numbers: Undocumented constants
  • Grouping: Related constants scattered

Details & Discussion:

// Current
const val EMAIL = "rafaelfelipeac@gmail.com" // Personal email exposed

// Suggested improvements
object ContactInfo {
    const val SUPPORT_EMAIL = "support@replyradar.com" // Generic email
    // Or load from build config/environment
}

object TimeConstants {
    /** Default reminder hour (1 PM) */
    const val DEFAULT_REMINDER_HOUR = 13
    /** Default reminder minute */
    const val DEFAULT_REMINDER_MINUTE = 0
    /** Offset for next day calculation */
    const val NEXT_DAY_OFFSET = 1
}

Why it matters: Production apps shouldn't expose personal emails. Documented constants improve maintainability.


4. ReplyRadarApplication.kt - Notification Channel

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/ReplyRadarApplication.kt

Issues:

  • Justify IMPORTANCE_HIGH for notification channel
  • Add error handling for notification channel creation

Details & Discussion:

// Current approach lacks justification and error handling
private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            // Why IMPORTANCE_HIGH? Maybe IMPORTANCE_DEFAULT is sufficient
            getString(R.string.notification_channel_id),
            getString(R.string.notification_channel_name),
            NotificationManager.IMPORTANCE_HIGH // ← Needs justification
        )
        // Add error handling here
    }
}

// Suggested improvement
private fun createNotificationChannel() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        try {
            val channel = NotificationChannel(
                getString(R.string.notification_channel_id),
                getString(R.string.notification_channel_name),
                // IMPORTANCE_DEFAULT unless reminders are truly urgent
                NotificationManager.IMPORTANCE_DEFAULT 
            ).apply {
                description = getString(R.string.notification_channel_description)
            }
            
            val notificationManager = getSystemService(NotificationManager::class.java)
            notificationManager?.createNotificationChannel(channel)
        } catch (e: Exception) {
            // Log error but don't crash the app
        }
    }
}

Why it matters: IMPORTANCE_HIGH makes notifications very intrusive. Error handling prevents app crashes.


5. FloatingActionButton.kt - Naming Conflict

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/FloatingActionButton.kt

Issue: Name conflicts with Material3's FloatingActionButton

Details:

// Current - causes import confusion
@Composable 
fun FloatingActionButton(...)

// Suggested alternatives
@Composable 
fun ReplyFloatingActionButton(...)
// or
@Composable 
fun AddReplyFab(...)

Why it matters: Prevents import confusion and makes the component's purpose clearer.


6. FakeReminderScheduler.kt - Test Utilities

File: composeApp/src/commonTest/kotlin/com/rafaelfelipeac/replyradar/fakes/core/util/FakeReminderScheduler.kt

Issue: Missing utility methods for test assertions

Details & Examples:

class FakeReminderScheduler : ReminderScheduler {
    private val _scheduledReminders = mutableListOf<Pair<Long, NotificationReminderParams>>()
    private val _cancelledReminders = mutableListOf<Long>()
    
    // Add these utility methods
    fun getScheduledReminders(): List<Pair<Long, NotificationReminderParams>> = _scheduledReminders.toList()
    fun getCancelledReminders(): List<Long> = _cancelledReminders.toList()
    
    fun wasReminderScheduled(replyId: Long): Boolean = 
        _scheduledReminders.any { it.second.replyId == replyId }
    
    fun wasReminderCancelled(replyId: Long): Boolean = 
        _cancelledReminders.contains(replyId)
        
    fun getScheduledReminderFor(replyId: Long): Pair<Long, NotificationReminderParams>? =
        _scheduledReminders.find { it.second.replyId == replyId }
        
    fun clear() {
        _scheduledReminders.clear()
        _cancelledReminders.clear()
    }
}

Why it matters: Makes tests more readable and assertions more precise.


7. DatetimeExt.kt - Timezone Consistency

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/DatetimeExt.kt

Issue: Potential timezone inconsistency

Details & Discussion:

// Current - might have timezone mismatches
fun LocalDate.toEpochMillis(): Long {
    return this.atStartOfDay(TimeZone.currentSystemDefault()).toEpochMilliseconds()
}

fun Long.toLocalDate(timeZone: TimeZone = TimeZone.currentSystemDefault()): LocalDate {
    return Instant.fromEpochMilliseconds(this).toLocalDateTime(timeZone).date
}

// Consider: Should both functions use the same timezone approach?
// Maybe add explicit timezone parameters for clarity

Why it matters: Timezone mismatches can cause date calculation errors, especially around daylight saving transitions.


8. RepliesScreen.kt - Layout Optimization

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/RepliesScreen.kt

Issue: Overly nested layout structure

Details:

// Current - deeply nested
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
    Column(modifier = Modifier.fillMaxSize().background(MaterialTheme.colorScheme.background)) {
        Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
            // Content
        }
    }
}

// Simplified alternative
Column(
    modifier = Modifier
        .fillMaxSize()
        .background(MaterialTheme.colorScheme.background)
) {
    // Direct content without extra Box wrapper
}

Why it matters: Reduces composition overhead and improves performance.


9. PlatformTimePicker.android.kt - UX Improvement

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/PlatformTimePicker.android.kt

Issue: Non-intuitive default time (12:12)

Details:

// Current default
val initialTime = selectedTime ?: LocalTime(12, 12)

// More intuitive alternatives
val initialTime = selectedTime ?: LocalTime(9, 0)  // 9:00 AM
// or current time + 1 hour
val initialTime = selectedTime ?: Clock.System.now()
    .toLocalDateTime(TimeZone.currentSystemDefault())
    .time.let { LocalTime(it.hour + 1, 0) }

Why it matters: Better user experience with sensible defaults.


10. ReminderSchedulerImpl.kt - Edge Cases & Performance

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/ReminderSchedulerImpl.kt

Issues:

  • Edge case for delay == 0
  • Resource optimization opportunities

Details & Examples:

// Current approach
private fun getDelayMillis(reminderAtMillis: Long): Long {
    return reminderAtMillis - System.currentTimeMillis()
}

// Improved with edge case handling
private fun getDelayMillis(reminderAtMillis: Long): Long {
    val delay = reminderAtMillis - System.currentTimeMillis()
    return when {
        delay <= 0 -> 0L // Handle immediate scheduling
        delay < MIN_DELAY_MS -> MIN_DELAY_MS // Minimum delay
        else -> delay
    }
}

// Resource optimization
companion object {
    private const val MIN_DELAY_MS = 1000L // 1 second minimum
    private const val REPLY_ID_KEY = "REPLY_ID_KEY" // Cache keys
    private const val NOTIFICATION_TITLE_KEY = "NOTIFICATION_TITLE_KEY"
    private const val NOTIFICATION_CONTENT_KEY = "NOTIFICATION_CONTENT_KEY"
}

Why it matters: Prevents edge case issues and improves performance through caching.


11. NotificationUtils.kt - Hash Collision Risk

File: composeApp/src/androidMain/kotlin/com/rafaelfelipeac/replyradar/core/reminder/NotificationUtils.kt

Issue: Potential hash collision with replyId.hashCode()

Details:

// Current - potential collision
NotificationManagerCompat.from(context).notify(replyId.hashCode(), notification)

// Safer alternatives
NotificationManagerCompat.from(context).notify(replyId.toInt(), notification)
// or if replyId might be too large
NotificationManagerCompat.from(context).notify(
    (replyId and 0x7FFFFFFF).toInt(), // Ensure positive int
    notification
)

Why it matters: Hash collisions could cause notification replacement issues.


12. ReplyTimestampInfo.kt - Logic Edge Case

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/components/ReplyTimestampInfo.kt

Issue: Edge case where updatedAt == createdAt but both are meaningful

Details:

// Current logic might miss some cases
private fun getTimestampInfo(reply: Reply): String {
    // Consider: What if updatedAt equals createdAt but both are set to meaningful times?
    // Current logic: updatedAt != createdAt
    // Better: updatedAt != INITIAL_DATE && updatedAt != createdAt
}

Why it matters: Ensures accurate timestamp display in all scenarios.


13. ReplyReminder.kt - State Management

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/common/ui/components/ReplyReminder.kt

Issue: Non-atomic state updates on delete

Details:

// Current - separate state updates
onDeleteClick = {
    onSelectedTimeChange(null)
    onSelectedDateChange(null)
    showTimePicker = false
    showDatePicker = false
    closeKeyboard?.invoke()
}

// Better - atomic update or use effect
onDeleteClick = {
    // Group related state changes
    onSelectedTimeChange(null)
    onSelectedDateChange(null)
    // Reset picker states
    showTimePicker = false
    showDatePicker = false
    closeKeyboard?.invoke()
}

Why it matters: Prevents inconsistent intermediate states during deletion.


14. Datetime.kt - Naming Clarity

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/core/datetime/Datetime.kt

Issue: Unclear constant naming

Details:

// Current
private const val LOCAL_TIME_HOUR_DEFAULT = 13

// Clearer alternatives
private const val DEFAULT_REMINDER_HOUR = 13 // 1 PM
// or with documentation
/** Default reminder time: 1:00 PM */
private const val DEFAULT_REMINDER_HOUR = 13

Why it matters: Self-documenting code reduces cognitive load.


15. ReplyListScreen.kt - Error Handling & Logic

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListScreen.kt

Issues:

  • Missing error handling for notification content formatting
  • Complex reminder validation logic

Details:

// Add error handling for string formatting
private fun getNotificationContent(strings: Strings, effect: ScheduleReminder): String {
    return try {
        if (effect.reply.subject.isNotBlank()) {
            strings.notificationContent.format(effect.reply.name, effect.reply.subject)
        } else {
            strings.notificationContentWithoutSubject.format(effect.reply.name)
        }
    } catch (e: Exception) {
        // Fallback content
        "Reminder: ${effect.reply.name}"
    }
}

// Simplify validation logic in onSaveReply
private fun onSaveReply(
    reply: Reply,
    onCheckNotificationPermission: (Reply) -> Unit,
    onAddOrEditReply: (Reply) -> Unit
) {
    val hasReminder = reply.reminderAt != INITIAL_DATE
    if (hasReminder) {
        onCheckNotificationPermission(reply)
    } else {
        onAddOrEditReply(reply)
    }
}

Why it matters: Prevents crashes from malformed strings and improves code readability.


16. ReplyListViewModel.kt - Type Safety & Logic

File: composeApp/src/commonMain/kotlin/com/rafaelfelipeac/replyradar/features/reply/presentation/replylist/ReplyListViewModel.kt

Issues:

  • Use nullable Long instead of INVALID_ID pattern
  • Redundant null checking

Details:

// Current approach with magic constant
private var pendingReplyId: Long? = null
// vs
private var pendingReplyId: Long = INVALID_ID

// Simplify null checking
private fun checkPendingReplyId(replies: List<Reply>, onTabSelection: () -> Unit) {
    pendingReplyId?.let { id ->
        replies.find { it.id == id }?.let { reply ->
            onTabSelection()
            onOpenReply(reply)
            // ... rest of logic
            pendingReplyId = null
        }
    }
}

Why it matters: Nullable types are more idiomatic Kotlin than magic constants. Reduces redundant checks.


📋 Implementation Priority Suggestions

High Priority (affects functionality/safety):

Medium Priority (code quality/maintainability):

Low Priority (style/optimization):

This comprehensive list should serve as your roadmap for addressing technical debt in future PRs! 🚀


🧠 Learnings used
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.

@rafaelfelipeac rafaelfelipeac merged commit 83aee09 into main Jul 28, 2025
2 checks passed
@rafaelfelipeac rafaelfelipeac deleted the feat/reminder branch July 28, 2025 19:17
@rafaelfelipeac rafaelfelipeac restored the feat/reminder branch July 28, 2025 19:34
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