Skip to content

Fix dark mode text visibility and card creation race condition#14

Merged
tpaulshippy merged 3 commits into
mainfrom
fix/dark-mode-and-card-creation
May 25, 2026
Merged

Fix dark mode text visibility and card creation race condition#14
tpaulshippy merged 3 commits into
mainfrom
fix/dark-mode-and-card-creation

Conversation

@tpaulshippy
Copy link
Copy Markdown
Owner

@tpaulshippy tpaulshippy commented May 25, 2026

Summary

Fixes two issues:

1. Dark mode text visibility (Frontend)

Text in the card edit form's TextInput fields was not visible in dark mode. The text color was defaulting to system colors, resulting in dark text on a dark background.

Fix: Added theme-aware text color using the useThemeColor hook to ensure text is visible in both light and dark modes.

2. Card creation IntegrityError (Backend)

When creating new flashcards concurrently, a race condition could cause IntegrityError: UNIQUE constraint failed: bots_flashcard.deck_id, bots_flashcard.order because multiple requests could calculate the same max_order value before any of them saved.

Fix: Added select_for_update() on the flashcard rows within the transaction to lock them during the order calculation, preventing concurrent requests from reading stale data.

Testing

  • All 30 existing API tests pass
  • Frontend API tests pass

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced reliability of concurrent flashcard creation to prevent data conflicts
  • Style

    • Applied current theme's text color to flashcard editor inputs for consistent visual appearance

Review Change Stack

- Add theme-aware text color to TextInput components in cardEdit.tsx
- Fix IntegrityError by locking flashcard rows during card creation to prevent race conditions on order field
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 25, 2026

Warning

Review limit reached

@tpaulshippy, we couldn't start this review because you've used your available PR reviews for now.

Your plan includes 1 review of capacity. Refill in 45 minutes and 33 seconds.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more review capacity refills, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than trial, open-source, and free plans. In all cases, review capacity refills continuously over time.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3ed43e03-b54e-4293-ab8d-3ea948813554

📥 Commits

Reviewing files that changed from the base of the PR and between 834a270 and 9db90a0.

📒 Files selected for processing (2)
  • back/bots/viewsets/flashcard_viewset.py
  • front/app/flashcards/cardEdit.tsx
📝 Walkthrough

Walkthrough

This PR adds database-level row locking to prevent race conditions during flashcard creation and applies theme-aware text colors to the CardEdit input fields. The backend change serializes order assignments; the frontend change ensures input text inherits the current theme's color scheme.

Changes

Backend Order Race Prevention

Layer / File(s) Summary
Flashcard order row locking in perform_create
back/bots/viewsets/flashcard_viewset.py
IntegrityError imported. perform_create now locks existing flashcard rows for the deck (ordered by descending order) before computing the new flashcard's order, preventing concurrent requests from generating duplicate order values.

Frontend Theme Color Application

Layer / File(s) Summary
Theme color for CardEdit text inputs
front/app/flashcards/cardEdit.tsx
useThemeColor hook imported and textColor variable derived from it. Both "Front" and "Back" TextInput components updated to apply { color: textColor } style, enabling theme-aware text rendering alongside existing input styling.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related PRs

  • tpaulshippy/bots#11: Addresses the same flashcard ordering race condition by introducing row-level locking with select_for_update() in the create flashcard transaction.
  • tpaulshippy/bots#10: Prevents concurrent flashcard order conflicts during creation using select_for_update() row locking.

Poem

🐰 A lock and a color, both timely and sweet,
One guards the order, makes concurrency neat,
One paints the input with theme's gentle hue,
Two simple fixes, but oh-so-true! 🎨

🚥 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 clearly and specifically identifies the two main changes: fixing dark mode text visibility and fixing a card creation race condition, matching both the frontend and backend modifications in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/dark-mode-and-card-creation

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.

@tpaulshippy tpaulshippy requested a review from Copilot May 25, 2026 18:53
Copy link
Copy Markdown
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: 1

🧹 Nitpick comments (1)
front/app/flashcards/cardEdit.tsx (1)

107-109: ⚡ Quick win

Make placeholder text theme-aware too.

The input text now adapts to theme, but placeholder text is still fixed to #888, which can be inconsistent/low-contrast across themes. Use a theme-derived placeholder color (e.g., via useThemeColor) for both fields.

💡 Proposed change
-  const textColor = useThemeColor({}, 'text');
+  const textColor = useThemeColor({}, "text");
+  const placeholderColor = useThemeColor({}, "icon");
...
             <TextInput
               style={[styles.input, styles.cardInput, { color: textColor }]}
               placeholder="Enter the question or term"
-              placeholderTextColor="`#888`"
+              placeholderTextColor={placeholderColor}
               value={cardFront}
               onChangeText={setCardFront}
               multiline
             />
...
             <TextInput
               style={[styles.input, styles.cardInput, { color: textColor }]}
               placeholder="Enter the answer or definition"
-              placeholderTextColor="`#888`"
+              placeholderTextColor={placeholderColor}
               value={cardBack}
               onChangeText={setCardBack}
               multiline
             />

Also applies to: 117-119

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@front/app/flashcards/cardEdit.tsx` around lines 107 - 109, The
placeholderTextColor is hardcoded to "`#888`"; update the CardEdit component to
derive a theme-aware placeholder color via useThemeColor (e.g., const
placeholderColor = useThemeColor({ light: '`#888`', dark: '`#BBB`' }, 'placeholder')
or similar) and pass placeholderColor to the placeholderTextColor prop on both
TextInput fields (the ones using styles.input / styles.cardInput and textColor).
Ensure you import/use useThemeColor where the inputs are defined and replace the
literal "`#888`" at both input locations with the derived placeholderColor.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@back/bots/viewsets/flashcard_viewset.py`:
- Line 5: The current create/update path in FlashcardViewSet that calls
serializer.save(...) can still raise IntegrityError for the
unique_flashcard_order_per_deck constraint; catch django.db.utils.IntegrityError
around the serializer.save call (in the methods handling create/update), log the
deck identifier and constraint name using the view's context (e.g.,
self.request.data.get('deck') or serializer.validated_data['deck']), and raise a
typed API error (e.g., return Response with 400 Bad Request or raise a DRF
ValidationError with a message like "Flashcard order must be unique per deck:
deck=<deck_id>") so the client receives a clear, descriptive error instead of a
500. Ensure the log includes the deck id and the constraint name
unique_flashcard_order_per_deck and that the exception handling covers both
create and update paths where serializer.save is invoked.

---

Nitpick comments:
In `@front/app/flashcards/cardEdit.tsx`:
- Around line 107-109: The placeholderTextColor is hardcoded to "`#888`"; update
the CardEdit component to derive a theme-aware placeholder color via
useThemeColor (e.g., const placeholderColor = useThemeColor({ light: '`#888`',
dark: '`#BBB`' }, 'placeholder') or similar) and pass placeholderColor to the
placeholderTextColor prop on both TextInput fields (the ones using styles.input
/ styles.cardInput and textColor). Ensure you import/use useThemeColor where the
inputs are defined and replace the literal "`#888`" at both input locations with
the derived placeholderColor.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 59406aaa-8c0e-4604-b8a4-b01b802075fa

📥 Commits

Reviewing files that changed from the base of the PR and between aacba5c and 834a270.

📒 Files selected for processing (2)
  • back/bots/viewsets/flashcard_viewset.py
  • front/app/flashcards/cardEdit.tsx

Comment thread back/bots/viewsets/flashcard_viewset.py
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR addresses two user-facing regressions: unreadable flashcard editor input text in dark mode (frontend) and occasional UNIQUE constraint failed errors when creating flashcards concurrently due to non-atomic MAX(order)+1 assignment (backend).

Changes:

  • Frontend: Applies theme-aware text color to flashcard edit form TextInput fields.
  • Backend: Attempts to prevent duplicate order values during concurrent flashcard creation by adding a locking query inside the create transaction.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
front/app/flashcards/cardEdit.tsx Adds theme-derived text color to flashcard edit inputs to improve dark mode visibility.
back/bots/viewsets/flashcard_viewset.py Adds a select_for_update() query intended to reduce flashcard order race conditions during create.
Comments suppressed due to low confidence (2)

back/bots/viewsets/flashcard_viewset.py:79

  • select_for_update() is ignored by Django’s SQLite backend (see back/server/settings.py uses django.db.backends.sqlite3), so this added query won’t actually lock anything in the default environment. That means the max_order calculation can still race and hit the unique_flashcard_order_per_deck constraint under concurrent requests. Consider a DB-agnostic fix such as retrying the max_order + save() inside the transaction on IntegrityError (recompute max and retry a few times), or switching to an atomic counter stored on Deck/separate sequence table instead of MAX(order) + 1.
            # Lock flashcard rows to prevent race conditions on order
            Flashcard.objects.filter(deck=deck).select_for_update().order_by('-order').first()
            
            max_order = Flashcard.objects.filter(deck=deck).aggregate(Max('order'))['order__max'] or -1
            serializer.save(deck=deck, order=max_order + 1)

front/app/flashcards/cardEdit.tsx:111

  • These TextInput instances are still hard-coding placeholderTextColor="#888". If the goal is full dark/light theme correctness, consider sourcing placeholder color from the theme as well (or using the existing ThemedTextInput, which centralizes this behavior).
            <TextInput
              style={[styles.input, styles.cardInput, { color: textColor }]}
              placeholder="Enter the question or term"
              placeholderTextColor="#888"
              value={cardFront}
              onChangeText={setCardFront}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from rest_framework.exceptions import NotFound
from django.db.models import Count, Max
from django.db import transaction
from django.db.utils import IntegrityError
Comment on lines 28 to 32
const [cardFront, setCardFront] = useState(front || "");
const [cardBack, setCardBack] = useState(back || "");
const textColor = useThemeColor({}, 'text');

useEffect(() => {
- Removed select_for_update() calls since SQLite doesn't support row locking
- Implemented retry logic (max 3 attempts) for concurrent card creation
- On IntegrityError, recompute max_order and retry instead of failing
- This is a DB-agnostic fix that works with both SQLite and PostgreSQL
- Replace TextInput with ThemedTextInput component
- Remove manual useThemeColor hook and textColor variable
- ThemedTextInput handles text and placeholder colors consistently
- Keeps theming logic DRY across the app
@tpaulshippy tpaulshippy merged commit c5d0138 into main May 25, 2026
4 checks passed
@tpaulshippy tpaulshippy deleted the fix/dark-mode-and-card-creation branch May 25, 2026 19:07
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.

2 participants