Skip to content

Add default note color setting#900

Open
Crustack wants to merge 4 commits intomainfrom
feat/630
Open

Add default note color setting#900
Crustack wants to merge 4 commits intomainfrom
feat/630

Conversation

@Crustack
Copy link
Owner

@Crustack Crustack commented Mar 7, 2026

Closes #630

Summary by CodeRabbit

  • New Features

    • Set a default color for new notes via long-press in color dialogs; default is marked with a star and synced when changing colors.
    • Note color now initializes from your saved default preference; color lists are deduplicated for consistent selection.
    • Color editor adds delete flow, live default toggling, and improved dialogs/tooltips.
  • Documentation

    • Updated translations coverage table and counts.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Adds user-default note color support: new preference to store default color, UI updates to mark/manage default (star icon, long-press to set/revert), color dialogs/adapters converted to ColorString with default handling, persistence/viewmodel updates to sync defaults, plus translation and resource changes.

Changes

Cohort / File(s) Summary
Translation Coverage
TRANSLATIONS.md
Updated total translations to 341 and recomputed per-language counts/percentages.
Preferences
app/src/main/java/com/philkes/notallyx/presentation/viewmodel/preference/NotallyXPreferences.kt
Added defaultNoteColor StringPreference and included it in reload/refresh.
Persistence/DAO
app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt
Removed LiveData method getAllColorsAsync(); suspend getAllColors() retained.
Activity visibility
app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt
Changed preferences visibility from protectedinternal.
Model / ViewModel
app/src/main/java/com/philkes/notallyx/presentation/viewmodel/NotallyModel.kt, .../BaseNoteModel.kt
Initialize model color from preference; when changing a note color, update defaultNoteColor if the old color matched the previous default.
Color flows
app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt, app/.../note/NoteActionHandler.kt
Convert color collections to Set<ColorString>/MutableSet; ensure default color is present in options.
Adapters / ViewHolders
app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorAdapter.kt, .../ColorVH.kt
Switch types from StringColorString; add defaultColor property; bind gains isNoteDefault flag and shows/hides DefaultColorIcon.
Color dialogs & extensions
app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt
Migrate Activity→AppCompatActivity; use Set<ColorString>; add default color observer, default set/revert long-press flows, delete flow, and lifecycle-aware dialog wiring.
Layouts
app/src/main/res/layout/dialog_color_picker.xml, app/src/main/res/layout/recycler_color.xml
Removed root top padding; added hidden DeleteColor button to picker; added @id/DefaultColorIcon ImageView in color item.
Drawables
app/src/main/res/drawable/star.xml, app/src/main/res/drawable/star_rate_filled.xml, app/src/main/res/drawable/dashed_background.xml
Added star vector drawables and dashed background drawable used for default indicator and UI.
Strings
app/src/main/res/values/strings.xml
Added default_color_hint, make_default, set_as_default_color, set_as_default_color_message; updated change_color_message to mention the default-star indicator.

Sequence Diagram

sequenceDiagram
    actor User
    participant UI as Color UI (Adapter / ColorVH)
    participant Dialog as Color Dialog (ColorActivityExtensions)
    participant Prefs as NotallyXPreferences
    participant VM as ViewModel (BaseNoteModel / NotallyModel)
    participant DB as Database (BaseNoteDao)

    User->>UI: Long-press color item
    UI->>Dialog: Open confirm/set-default dialog
    User->>Dialog: Confirm set as default
    Dialog->>Prefs: set defaultNoteColor.value(newColor)
    Prefs-->>VM: notify observers (default changed)
    VM->>DB: persist color/default updates (if required)
    DB-->>VM: ack
    VM-->>UI: refresh displayed default state (star icon)
    Dialog-->>UI: update dialog (neutral text/icon)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~28 minutes

Possibly related PRs

Poem

🐇 I nudged a color with a press,
A tiny star now marks success.
New notes wear my chosen light,
Long-press set it — cozy and right.
Hop, clap, the default shines so bright. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add default note color setting' accurately summarizes the main change - introducing a default note color feature with settings UI support.
Linked Issues check ✅ Passed The changes implement all requirements from #630: adding a settings preference for default note color (NotallyXPreferences.defaultNoteColor), applying it to new notes (NotallyModel initialization), and providing UI controls via ColorActivityExtensions.
Out of Scope Changes check ✅ Passed All changes are scoped to implementing the default note color feature. Refactoring from String to ColorString type, DAO method cleanup, and UI enhancements (star icon, dashed background) are directly tied to supporting the core objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/630

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

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@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: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt (1)

35-58: ⚠️ Potential issue | 🟠 Major

Expose the default-state on the swatch, not only via the star icon.

CardView is the accessible click target here, but its contentDescription is still just color. That means the new default marker is visual-only for TalkBack users. Please include the default state in the swatch announcement/state description and treat the icon itself as decorative.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt`
around lines 35 - 58, CardView's contentDescription currently only contains the
color and doesn't announce the default state, leaving TalkBack users unaware;
update the code that sets CardView.contentDescription to include the default
marker when isDefault is true (e.g., append or build a localized string like
"ColorName, default" using the same logic that reads isDefault), and make the
DefaultColorIcon decorative for accessibility by clearing its contentDescription
(or setting it as not important for accessibility) while keeping CardIcon
behavior unchanged; reference CardView, DefaultColorIcon, CardIcon,
contentDescription, isDefault and showAddIcon when making these changes.
🧹 Nitpick comments (3)
app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt (1)

273-281: Consider simplifying the intermediate set conversion.

The inner .toMutableSet() at line 279 is redundant since the + operator returns an immutable Set regardless of the input type, and the outer .toMutableSet() at line 281 handles the final conversion.

♻️ Simplified version
 val colors: MutableSet<ColorString> =
     (withContext(Dispatchers.IO) {
             NotallyDatabase.getDatabase(activity, observePreferences = false)
                 .value
                 .getBaseNoteDao()
                 .getAllColors()
-                .toMutableSet()
-        } + activity.preferences.defaultNoteColor.value)
-        .toMutableSet()
+        }.toSet() + activity.preferences.defaultNoteColor.value)
+            .toMutableSet()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt`
around lines 273 - 281, The temporary call to .toMutableSet() inside the
withContext block is redundant because the set concatenation using + returns an
immutable Set which you then convert to mutable with the outer .toMutableSet();
update the code in NoteActionHandler (where the local val colors is built inside
the withContext(Dispatchers.IO) block calling
NotallyDatabase.getDatabase(...).getBaseNoteDao().getAllColors()) by removing
the inner .toMutableSet() so only the final .toMutableSet() is used to produce
the MutableSet<ColorString>.
app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt (1)

631-637: Good synchronization of default color when a color is deleted/changed.

The logic correctly updates the defaultNoteColor preference when the deleted color matches the current default. This prevents orphaned default color references.

Minor consistency note: The direct preferences.defaultNoteColor.save(newColor) call differs from the savePreference() pattern used elsewhere (line 392-394) that wraps in Dispatchers.IO. While SharedPreferences commits are typically fast, consider using the existing pattern for consistency:

♻️ Optional refactor for consistency
 fun changeColor(oldColor: String, newColor: String) {
     val defaultColor = preferences.defaultNoteColor.value
     if (oldColor == defaultColor) {
-        preferences.defaultNoteColor.save(newColor)
+        savePreference(preferences.defaultNoteColor, newColor)
     }
     viewModelScope.launch(Dispatchers.IO) { baseNoteDao.updateColor(oldColor, newColor) }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt`
around lines 631 - 637, changeColor in BaseNoteModel directly calls
preferences.defaultNoteColor.save(newColor) on the calling thread; for
consistency with other code (e.g., the savePreference(...) pattern used
elsewhere) move this preference write onto the IO dispatcher — either call the
existing savePreference(preferences.defaultNoteColor, newColor) helper or wrap
the save(...) call in viewModelScope.launch(Dispatchers.IO) {
preferences.defaultNoteColor.save(newColor) } so the default preference update
matches the IO-dispatched pattern used in this class.
app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt (1)

71-86: Consider whether long-pressing on COLOR_DEFAULT should behave differently.

Currently, long-pressing the transparent/no-color option (COLOR_DEFAULT) prompts to set it as the default color for new notes. While functionally correct, users might find it confusing that "no color" can be the "default color."

If this is intentional (allowing users to reset default to "no color"), the UX is acceptable. Otherwise, consider showing a different message or skipping this dialog entirely for COLOR_DEFAULT.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 71 - 86, Long-pressing the transparent option
(BaseNote.COLOR_DEFAULT) currently shows the same "set as default color" dialog
which can be confusing; update the long-press branch inside
showColorSelectDialog to either skip showing a dialog for BaseNote.COLOR_DEFAULT
or present a clearer confirmation that the default will be set to "no color"
(use new string resources like R.string.clear_default_color_title and
R.string.clear_default_color_message if needed). If you keep a dialog, call
preferences.defaultNoteColor.save(BaseNote.COLOR_DEFAULT) and set
colorAdapter.defaultColor = BaseNote.COLOR_DEFAULT then notify via
colorAdapter.notifyDataSetChanged(); if you prefer skipping, simply return
without showing MaterialAlertDialogBuilder when oldColor ==
BaseNote.COLOR_DEFAULT. Ensure you reference BaseNote.COLOR_DEFAULT,
showColorSelectDialog, preferences.defaultNoteColor.save, and
colorAdapter.defaultColor in the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/res/layout/dialog_color_picker.xml`:
- Around line 24-31: The DeleteColor ImageButton is visible by default but only
enabled in the old-color flow; change its default visibility to hidden so it
doesn't appear as a dead action in new-color flow: update the DeleteColor view
(android:id="@+id/DeleteColor") in the layout to use a non-visible default
(e.g., GONE or INVISIBLE) and let showEditColorDialog() continue to set it
visible inside the oldColor?.let { ... } block when appropriate.

In `@app/src/main/res/layout/recycler_color.xml`:
- Around line 29-37: The ImageView with id DefaultColorIcon in
recycler_color.xml uses a hardcoded contentDescription "DefaultNoteColor" which
prevents localization; replace the inline string by pointing contentDescription
to a string resource (e.g., `@string/default_note_color`) defined in strings.xml,
or if the parent swatch already exposes the default state to accessibility, make
the icon decorative by setting android:contentDescription="@null" and
android:importantForAccessibility="no" on DefaultColorIcon so it doesn't
duplicate the announcement.

---

Outside diff comments:
In `@app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt`:
- Around line 35-58: CardView's contentDescription currently only contains the
color and doesn't announce the default state, leaving TalkBack users unaware;
update the code that sets CardView.contentDescription to include the default
marker when isDefault is true (e.g., append or build a localized string like
"ColorName, default" using the same logic that reads isDefault), and make the
DefaultColorIcon decorative for accessibility by clearing its contentDescription
(or setting it as not important for accessibility) while keeping CardIcon
behavior unchanged; reference CardView, DefaultColorIcon, CardIcon,
contentDescription, isDefault and showAddIcon when making these changes.

---

Nitpick comments:
In
`@app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt`:
- Around line 273-281: The temporary call to .toMutableSet() inside the
withContext block is redundant because the set concatenation using + returns an
immutable Set which you then convert to mutable with the outer .toMutableSet();
update the code in NoteActionHandler (where the local val colors is built inside
the withContext(Dispatchers.IO) block calling
NotallyDatabase.getDatabase(...).getBaseNoteDao().getAllColors()) by removing
the inner .toMutableSet() so only the final .toMutableSet() is used to produce
the MutableSet<ColorString>.

In
`@app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt`:
- Around line 631-637: changeColor in BaseNoteModel directly calls
preferences.defaultNoteColor.save(newColor) on the calling thread; for
consistency with other code (e.g., the savePreference(...) pattern used
elsewhere) move this preference write onto the IO dispatcher — either call the
existing savePreference(preferences.defaultNoteColor, newColor) helper or wrap
the save(...) call in viewModelScope.launch(Dispatchers.IO) {
preferences.defaultNoteColor.save(newColor) } so the default preference update
matches the IO-dispatched pattern used in this class.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`:
- Around line 71-86: Long-pressing the transparent option
(BaseNote.COLOR_DEFAULT) currently shows the same "set as default color" dialog
which can be confusing; update the long-press branch inside
showColorSelectDialog to either skip showing a dialog for BaseNote.COLOR_DEFAULT
or present a clearer confirmation that the default will be set to "no color"
(use new string resources like R.string.clear_default_color_title and
R.string.clear_default_color_message if needed). If you keep a dialog, call
preferences.defaultNoteColor.save(BaseNote.COLOR_DEFAULT) and set
colorAdapter.defaultColor = BaseNote.COLOR_DEFAULT then notify via
colorAdapter.notifyDataSetChanged(); if you prefer skipping, simply return
without showing MaterialAlertDialogBuilder when oldColor ==
BaseNote.COLOR_DEFAULT. Ensure you reference BaseNote.COLOR_DEFAULT,
showColorSelectDialog, preferences.defaultNoteColor.save, and
colorAdapter.defaultColor in the change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9ca35dba-448d-41ca-ab83-f2de10482293

📥 Commits

Reviewing files that changed from the base of the PR and between 19c66c1 and a1dff14.

⛔ Files ignored due to path filters (1)
  • app/translations.xlsx is excluded by !**/*.xlsx
📒 Files selected for processing (16)
  • TRANSLATIONS.md
  • app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/LockedActivity.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt
  • app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorAdapter.kt
  • app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt
  • app/src/main/java/com/philkes/notallyx/presentation/viewmodel/BaseNoteModel.kt
  • app/src/main/java/com/philkes/notallyx/presentation/viewmodel/NotallyModel.kt
  • app/src/main/java/com/philkes/notallyx/presentation/viewmodel/preference/NotallyXPreferences.kt
  • app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt
  • app/src/main/res/drawable/star.xml
  • app/src/main/res/drawable/star_rate_filled.xml
  • app/src/main/res/layout/dialog_color_picker.xml
  • app/src/main/res/layout/recycler_color.xml
  • app/src/main/res/values/strings.xml
💤 Files with no reviewable changes (1)
  • app/src/main/java/com/philkes/notallyx/data/dao/BaseNoteDao.kt

Copy link
Contributor

@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: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`:
- Around line 74-83: The code is persisting the sentinel BaseNote.COLOR_DEFAULT
into preferences via preferences.defaultNoteColor.save(oldColor), which stores a
placeholder instead of a concrete swatch and can break consumers that expect an
actual color string; change the branch so it does not save the sentinel value —
either resolve the real swatch before saving (e.g. determine the concrete color
from the adapter/item list and call
preferences.defaultNoteColor.save(resolvedColor) and set
colorAdapter.defaultColor = resolvedColor) or simply skip
preferences.defaultNoteColor.save when oldColor == BaseNote.COLOR_DEFAULT and
only update the UI state (colorAdapter.defaultColor and notifyDataSetChanged).
- Around line 215-259: The neutral button is only updated when
preferences.defaultNoteColor changes and its "revert" path calls
performLongClick() with no long-click listener, making it stale and a no-op
after picker moves; fix by wiring the neutral Button (found via
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)) to be updated whenever
binding.ColorPicker.colorEnvelope changes (reuse the existing
ColorPicker.setColorListener or the observer callback to recompute
isDefaultColor, set text/icon/tooltips and onClick), and implement a real revert
action: store the dialog's initial color (e.g., oldColor/initialColor) and have
the neutral button's onClick restore that color into
binding.ColorPicker.setColor / setPaintColor and update ColorCode and
preferences.defaultNoteColor.save as appropriate so the button stays in sync and
actually reverts.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1223abb2-3241-46eb-b1b4-20c4e3b39a44

📥 Commits

Reviewing files that changed from the base of the PR and between a1dff14 and c1a6c1f.

📒 Files selected for processing (5)
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt
  • app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt
  • app/src/main/res/layout/dialog_color_picker.xml
  • app/src/main/res/layout/recycler_color.xml
🚧 Files skipped from review as they are similar to previous changes (3)
  • app/src/main/java/com/philkes/notallyx/presentation/activity/main/MainActivity.kt
  • app/src/main/res/layout/recycler_color.xml
  • app/src/main/java/com/philkes/notallyx/presentation/activity/note/NoteActionHandler.kt

Comment on lines +74 to +83
if (oldColor == BaseNote.COLOR_DEFAULT) {
MaterialAlertDialogBuilder(this@showColorSelectDialog)
.setTitle(R.string.set_as_default_color)
.setMessage(R.string.set_as_default_color_message)
.setPositiveButton(R.string.make_default) { _, _ ->
preferences.defaultNoteColor.save(oldColor)
colorAdapter.defaultColor = oldColor
colorAdapter.notifyDataSetChanged()
}
.setNegativeButton(android.R.string.cancel, null)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Don't persist BaseNote.COLOR_DEFAULT as the preference value.

Line 79 saves the placeholder entry, not a concrete swatch. That can overwrite defaultNoteColor with the sentinel token and break later code paths that expect an actual color string. This branch should resolve the real default color value before saving, or avoid writing the preference here altogether.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 74 - 83, The code is persisting the sentinel BaseNote.COLOR_DEFAULT
into preferences via preferences.defaultNoteColor.save(oldColor), which stores a
placeholder instead of a concrete swatch and can break consumers that expect an
actual color string; change the branch so it does not save the sentinel value —
either resolve the real swatch before saving (e.g. determine the concrete color
from the adapter/item list and call
preferences.defaultNoteColor.save(resolvedColor) and set
colorAdapter.defaultColor = resolvedColor) or simply skip
preferences.defaultNoteColor.save when oldColor == BaseNote.COLOR_DEFAULT and
only update the UI state (colorAdapter.defaultColor and notifyDataSetChanged).

Comment on lines +215 to +259
ColorPicker.setColorListener(
ColorEnvelopeListener { color, _ ->
TileView.setPaintColor(color.color)
val colorString = color.toColorString()
val isSaveEnabled =
colorString == oldColor || colorString !in colors
positiveButton.isEnabled = isSaveEnabled
ColorExistsText.visibility =
if (isSaveEnabled) View.GONE else View.VISIBLE
if (!editTextChangedByUser) {
ColorCode.setText(color.hexCode.argbToRgbString())
} else editTextChangedByUser = false
}
)
}
},
)
}
showAndFocus(
allowFullSize = true,
onShowListener = {
setNavigationbarLight?.let {
window?.apply { setLightStatusAndNavBar(it, binding.root) }
}
},
applyToPositiveButton = { positiveButton ->
binding.apply {
BrightnessSlideBar.setSelectorDrawableRes(
com.skydoves.colorpickerview.R.drawable.colorpickerview_wheel
)
ColorPicker.setColorListener(
ColorEnvelopeListener { color, _ ->
TileView.setPaintColor(color.color)
val colorString = color.toColorString()
val isSaveEnabled = colorString == oldColor || colorString !in colors
positiveButton.isEnabled = isSaveEnabled
ColorExistsText.visibility =
if (isSaveEnabled) View.INVISIBLE else View.VISIBLE
if (!editTextChangedByUser) {
ColorCode.setText(color.hexCode.argbToRgbString())
} else editTextChangedByUser = false
}
)
val observer: Observer<ColorString> = Observer { defaultColor ->
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.apply {
val currentColor = binding.ColorPicker.colorEnvelope.toColorString()
val isDefaultColor = currentColor == defaultColor
setText(if (isDefaultColor) R.string.text_default else R.string.make_default)
if (!isDefaultColor) {
TooltipCompat.setTooltipText(this, getString(R.string.set_as_default_color_message))
setOnClickListener {
val newColor = binding.ColorPicker.colorEnvelope.toColorString()
preferences.defaultNoteColor.save(newColor)
}
},
)
} else {
TooltipCompat.setTooltipText(this, getString(R.string.default_color_hint))
setOnClickListener { performLongClick() }
}
(this as? MaterialButton)?.apply {
icon =
if (isDefaultColor)
ContextCompat.getDrawable(
this@showEditColorDialog,
R.drawable.star_rate_filled,
)
else ContextCompat.getDrawable(this@showEditColorDialog, R.drawable.star)
iconGravity = MaterialButton.ICON_GRAVITY_TEXT_START
iconPadding = 0
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Keep the neutral button in sync with the picker, and wire a real revert action.

This button is only refreshed when defaultNoteColor changes, so after the user moves the picker it can keep stale text/icon/click behavior. In the isDefaultColor branch, Line 246 routes clicks to performLongClick(), but no long-click listener is attached here, so the control becomes a no-op.

Suggested direction
+    fun updateDefaultButton(defaultColor: ColorString = preferences.defaultNoteColor.value) {
+        dialog?.getButton(AlertDialog.BUTTON_NEUTRAL)?.apply {
+            val currentColor = binding.ColorPicker.colorEnvelope.toColorString()
+            val isDefaultColor = currentColor == defaultColor
+            setText(if (isDefaultColor) R.string.text_default else R.string.make_default)
+            TooltipCompat.setTooltipText(
+                this,
+                getString(
+                    if (isDefaultColor) R.string.default_color_hint
+                    else R.string.set_as_default_color_message
+                )
+            )
+            setOnClickListener {
+                if (!isDefaultColor) {
+                    preferences.defaultNoteColor.save(currentColor)
+                }
+            }
+            setOnLongClickListener {
+                if (isDefaultColor) {
+                    // show revert/reset-default dialog here
+                    true
+                } else {
+                    false
+                }
+            }
+        }
+    }
...
                             ColorEnvelopeListener { color, _ ->
                                 TileView.setPaintColor(color.color)
                                 val colorString = color.toColorString()
                                 val isSaveEnabled =
                                     colorString == oldColor || colorString !in colors
                                 positiveButton.isEnabled = isSaveEnabled
                                 ColorExistsText.visibility =
                                     if (isSaveEnabled) View.GONE else View.VISIBLE
+                                updateDefaultButton()
                                 if (!editTextChangedByUser) {
                                     ColorCode.setText(color.hexCode.argbToRgbString())
                                 } else editTextChangedByUser = false
                             }
...
-    val observer: Observer<ColorString> = Observer { defaultColor ->
-        dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.apply {
-            ...
-        }
-    }
+    val observer: Observer<ColorString> = Observer { defaultColor ->
+        updateDefaultButton(defaultColor)
+    }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 215 - 259, The neutral button is only updated when
preferences.defaultNoteColor changes and its "revert" path calls
performLongClick() with no long-click listener, making it stale and a no-op
after picker moves; fix by wiring the neutral Button (found via
dialog.getButton(AlertDialog.BUTTON_NEUTRAL)) to be updated whenever
binding.ColorPicker.colorEnvelope changes (reuse the existing
ColorPicker.setColorListener or the observer callback to recompute
isDefaultColor, set text/icon/tooltips and onClick), and implement a real revert
action: store the dialog's initial color (e.g., oldColor/initialColor) and have
the neutral button's onClick restore that color into
binding.ColorPicker.setColor / setPaintColor and update ColorCode and
preferences.defaultNoteColor.save as appropriate so the button stays in sync and
actually reverts.

Copy link
Contributor

@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.

♻️ Duplicate comments (2)
app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt (2)

75-92: ⚠️ Potential issue | 🔴 Critical

Persisting sentinel COLOR_DEFAULT as the preference value corrupts default color state.

When oldColor == BaseNote.COLOR_DEFAULT (the sentinel tile), this branch can save the sentinel itself into preferences.defaultNoteColor (line 84). Downstream code expecting an actual hex color string will break.

The logic here appears inverted—the COLOR_DEFAULT tile represents "use the preference," not a color that can become the new default. Consider removing this branch entirely or re-evaluating the UX intent: long-pressing the default placeholder tile shouldn't prompt to "set as default."

,

Suggested fix: remove sentinel-saving path
                     if (oldColor == BaseNote.COLOR_DEFAULT) {
-                        if (oldColor == preferences.defaultNoteColor.value) {
-                            showToast(R.string.default_color_hint)
-                            return
-                        } else {
-                            MaterialAlertDialogBuilder(this@showColorSelectDialog)
-                                .setTitle(R.string.set_as_default_color)
-                                .setMessage(R.string.set_as_default_color_message)
-                                .setPositiveButton(R.string.make_default) { _, _ ->
-                                    preferences.defaultNoteColor.save(oldColor)
-                                    colorAdapter.defaultColor = oldColor
-                                    colorAdapter.notifyItemRangeChanged(1, actualColors.size - 1)
-                                }
-                                .setNegativeButton(android.R.string.cancel, null)
-                                .show()
-                            return
-                        }
+                        // The default placeholder tile cannot be set as default itself
+                        showToast(R.string.default_color_hint)
+                        return
                     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 75 - 92, The branch inside showColorSelectDialog that handles
oldColor == BaseNote.COLOR_DEFAULT is saving the sentinel value into preferences
(via preferences.defaultNoteColor.save(oldColor)) which corrupts default color
state; remove or change this branch so long-pressing the sentinel tile does not
prompt to "set as default" or save BaseNote.COLOR_DEFAULT—either delete the
entire if (oldColor == BaseNote.COLOR_DEFAULT) block or short-circuit it to just
return/ignore, and ensure you do not call preferences.defaultNoteColor.save(...)
or update colorAdapter.defaultColor/notifyItemRangeChanged with the sentinel
value.

239-265: ⚠️ Potential issue | 🟠 Major

Neutral button becomes stale after picker moves; performLongClick() is a no-op.

Two issues remain from prior review:

  1. Stale state: The observer (line 239) only fires when defaultNoteColor LiveData changes. After the user drags the color picker, currentColor changes but the button text/icon/click-handler stay frozen until the next preference emission.

  2. No-op click: Line 252 calls performLongClick(), but no setOnLongClickListener is registered on this button, so the click does nothing.

,

Suggested direction

Extract the button-update logic into a local function and call it both from the observer and from the ColorEnvelopeListener callback (lines 221-234):

fun updateNeutralButton(defaultColor: ColorString) {
    dialog.getButton(AlertDialog.BUTTON_NEUTRAL)?.apply {
        val currentColor = binding.ColorPicker.colorEnvelope.toColorString()
        val isDefaultColor = currentColor == defaultColor
        setText(if (isDefaultColor) R.string.text_default else R.string.make_default)
        // ... icon, tooltip ...
        setOnClickListener {
            if (!isDefaultColor) {
                preferences.defaultNoteColor.save(currentColor)
            }
        }
        // Either implement a real long-click action or remove the branch
    }
}

Then invoke updateNeutralButton(preferences.defaultNoteColor.value) inside ColorEnvelopeListener after updating positiveButton.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 239 - 265, The neutral button UI/code is stale because the Observer
on preferences.defaultNoteColor only updates when the LiveData emits and
performLongClick() is a no-op; extract the button update logic into a helper
like updateNeutralButton(defaultColor: ColorString) that reads
binding.ColorPicker.colorEnvelope.toColorString(), sets text/icon/tooltip, and
installs a real setOnClickListener that calls
preferences.defaultNoteColor.save(currentColor) when not default (and either
remove the performLongClick branch or implement setOnLongClickListener for the
intended behavior); call updateNeutralButton(preferences.defaultNoteColor.value)
from both the Observer (observer: Observer<ColorString>) and from the
ColorEnvelopeListener callback (after you update the positive button) so the
neutral button reflects picker moves immediately.
🧹 Nitpick comments (1)
app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt (1)

271-276: Use ColorString type consistently across function signatures.

showDeleteColorDialog uses List<String> and String parameters while showColorSelectDialog and showEditColorDialog use ColorString. Since ColorString is a typealias for String, this compiles but weakens type semantics and makes the API inconsistent.

Suggested alignment
-private fun AppCompatActivity.showDeleteColorDialog(
-    colors: List<String>,
-    oldColor: String,
-    setNavigationbarLight: Boolean?,
-    callback: (selectedColor: String, oldColor: String?) -> Unit,
-    deleteCallback: (colorToDelete: String, newColor: String) -> Unit,
+private fun AppCompatActivity.showDeleteColorDialog(
+    colors: List<ColorString>,
+    oldColor: ColorString,
+    setNavigationbarLight: Boolean?,
+    callback: (selectedColor: ColorString, oldColor: ColorString?) -> Unit,
+    deleteCallback: (colorToDelete: ColorString, newColor: ColorString) -> Unit,
 ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`
around lines 271 - 276, The function showDeleteColorDialog should use the
ColorString typealias instead of raw String to match other APIs; update its
signature to accept colors: List<ColorString>, oldColor: ColorString (nullable
if previously nullable), and change the callback and deleteCallback parameter
types to use ColorString/ColorString? (i.e., callback: (selectedColor:
ColorString, oldColor: ColorString?) -> Unit and deleteCallback: (colorToDelete:
ColorString, newColor: ColorString) -> Unit). After changing the signature in
showDeleteColorDialog, update any call sites to pass/expect ColorString types
accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`:
- Around line 75-92: The branch inside showColorSelectDialog that handles
oldColor == BaseNote.COLOR_DEFAULT is saving the sentinel value into preferences
(via preferences.defaultNoteColor.save(oldColor)) which corrupts default color
state; remove or change this branch so long-pressing the sentinel tile does not
prompt to "set as default" or save BaseNote.COLOR_DEFAULT—either delete the
entire if (oldColor == BaseNote.COLOR_DEFAULT) block or short-circuit it to just
return/ignore, and ensure you do not call preferences.defaultNoteColor.save(...)
or update colorAdapter.defaultColor/notifyItemRangeChanged with the sentinel
value.
- Around line 239-265: The neutral button UI/code is stale because the Observer
on preferences.defaultNoteColor only updates when the LiveData emits and
performLongClick() is a no-op; extract the button update logic into a helper
like updateNeutralButton(defaultColor: ColorString) that reads
binding.ColorPicker.colorEnvelope.toColorString(), sets text/icon/tooltip, and
installs a real setOnClickListener that calls
preferences.defaultNoteColor.save(currentColor) when not default (and either
remove the performLongClick branch or implement setOnLongClickListener for the
intended behavior); call updateNeutralButton(preferences.defaultNoteColor.value)
from both the Observer (observer: Observer<ColorString>) and from the
ColorEnvelopeListener callback (after you update the positive button) so the
neutral button reflects picker moves immediately.

---

Nitpick comments:
In `@app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt`:
- Around line 271-276: The function showDeleteColorDialog should use the
ColorString typealias instead of raw String to match other APIs; update its
signature to accept colors: List<ColorString>, oldColor: ColorString (nullable
if previously nullable), and change the callback and deleteCallback parameter
types to use ColorString/ColorString? (i.e., callback: (selectedColor:
ColorString, oldColor: ColorString?) -> Unit and deleteCallback: (colorToDelete:
ColorString, newColor: ColorString) -> Unit). After changing the signature in
showDeleteColorDialog, update any call sites to pass/expect ColorString types
accordingly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9290e8c4-f030-420a-8a9c-d775a856010e

📥 Commits

Reviewing files that changed from the base of the PR and between c1a6c1f and 6ff6a33.

📒 Files selected for processing (3)
  • app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt
  • app/src/main/java/com/philkes/notallyx/utils/ColorActivityExtensions.kt
  • app/src/main/res/drawable/dashed_background.xml
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/com/philkes/notallyx/presentation/view/main/ColorVH.kt

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.

Being able to select a color by default for new notes

1 participant