Skip to content

Conversation

@zonetecde
Copy link
Collaborator

@zonetecde zonetecde commented Oct 22, 2025

Summary

Fixes #QF-2639 & QF-239

  • Fix issue where values were not persisted between modal openings

  • Fix RepeatAudioModal to correctly load settings:

    • Use values stored in the database when logged in
    • Use values from Redux storage when not logged in
    • Fallback to default values otherwise
  • Add safeguard to automatically swap start and end verses when they are in the wrong order

Need PR 764 of BE to works entirely.

Type of change

  • Bug fix (non-breaking change which fixes an issue)

Test plan

I've made several Playwright tests covering all these cases:

  • Confirm that opening the repeat modal correctly displays its content.
  • Ensure that repeat settings (start verse, end verse, playback range, repeat count, delay) remain consistent when reopening the modal.
  • Check that values stay saved after reloading the page or switching between chapters.
  • Validate that returning to a previously visited surah restores the same repeat settings.
  • Confirm that preferences are correctly stored in the backend and reloaded for logged-in users across sessions.

They are included in the following commits:

Checklist

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • My changes generate no new warnings
  • Any dependent changes have been merged and published in downstream modules
  • I have commented on my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Screenshots or videos

Before After
image image

Summary by CodeRabbit

  • Bug Fixes

    • Safer, normalized repeat ranges to prevent invalid repeat behavior.
    • Reset repeat state reliably when opening the repeat modal.
  • Improvements

    • Repeat preferences persist and restore more consistently.
    • Applying settings now serializes, persists, and can immediately start playback.
    • Single-verse and repetition-mode interactions produce consistent ranges.
  • Chores

    • Internal preference/type handling updated for repeat settings storage.

* Use values stored in the database when logged in
* Use values from Redux storage when not logged in
* Fallback to default values otherwise
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 22, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed The pull request description comprehensively follows the repository template, including all required sections: a clear Summary with JIRA ticket references and description of the fix, properly marked Type of change (Bug fix), a detailed Test plan that references specific Playwright test commits and describes test coverage, and a completed Checklist with all items addressed (seven checked with one unchecked for documentation, which is justified). The description also includes before/after screenshots demonstrating the change, providing visual context for reviewers.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title clearly identifies the main change: fixing RepeatAudioModal to correctly load repeat settings, with specific issue references (QF-2639 & QF-239).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch QF-2639

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

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 115 to 420
const onPlayClick = () => {
logButtonClick('start_repeat_play');
onSettingsChangeWithoutDispatch('repeatSettings', verseRepetition, PreferenceGroup.AUDIO, play);
const normalizedRange = normalizeVerseRange(verseRepetition.from, verseRepetition.to);
const normalizedSettings = {
...verseRepetition,
...normalizedRange,
};
setVerseRepetition(normalizedSettings);
onSettingsChangeWithoutDispatch(
'repeatSettings',
normalizedSettings,
PreferenceGroup.AUDIO,
() => play(normalizedSettings),

Choose a reason for hiding this comment

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

P1 Badge Persist repeat settings into Redux when saving

The modal now reads audioPlayerState.repeatSettings to populate defaults once the repeat actor is gone, but onPlayClick persists changes via onSettingsChangeWithoutDispatch, which only calls the backend and invokes the callback. No Redux action updates audioPlayerState.repeatSettings, so for anonymous users (and until a server sync occurs for authenticated ones) the slice remains undefined and the modal still resets to defaults after closing. Dispatch a local update when saving so the newly added Redux-based fallback actually stores the settings.

Useful? React with 👍 / 👎.

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

Caution

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

⚠️ Outside diff range comments (3)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (3)

434-442: Fix analytics key typo and avoid assigning possibly undefined verse keys.

  • Typo: repitition_moderepetition_mode.
  • When Single mode and selectedVerseKey is undefined, from/to become undefined (state type says string). Use safe fallbacks and normalize.
-  logValueChange('repitition_mode', repetitionMode, mode);
-  setVerseRepetition((prevVerseRepetition) => ({
-    ...prevVerseRepetition,
-    from: mode === RepetitionMode.Single ? selectedVerseKey : firstVerseKeyInThisChapter,
-    to: mode === RepetitionMode.Single ? selectedVerseKey : lastVerseKeyInThisChapter,
-  }));
+  logValueChange('repetition_mode', repetitionMode, mode);
+  setVerseRepetition((prev) => {
+    const singleKey = selectedVerseKey ?? prev.from;
+    const nextFrom =
+      mode === RepetitionMode.Single
+        ? singleKey
+        : firstVerseKeyInThisChapter ?? prev.from;
+    const nextTo =
+      mode === RepetitionMode.Single
+        ? singleKey
+        : lastVerseKeyInThisChapter ?? prev.to;
+    const normalized = normalizeVerseRange(nextFrom, nextTo);
+    return { ...prev, ...normalized };
+  });

This prevents undefined state and preserves correct ordering.


444-448: Analytics old value is wrong dimension.

Logging uses repeatRange as the “old value” when changing a single verse. Use the previous verse key.

-  logValueChange('repeat_single_verse', verseRepetition.repeatRange, verseKey);
+  logValueChange('repeat_single_verse', verseRepetition.from, verseKey);

This keeps analytics consistent. As per coding guidelines.


465-478: Add explicit parameter types to handlers.

Tighten types per guidelines; avoid implicit any.

-const onSingleVerseChange = (verseKey) => {
+const onSingleVerseChange = (verseKey: string) => {
   ...
 };
-const onRangeChange = (range) => {
+const onRangeChange = (range: Partial<{ from: string; to: string }>) => {
   ...
 };
-const onRepeatRangeChange = (val) => {
+const onRepeatRangeChange = (val: number) => {
   ...
 };
-const onRepeatEachVerseChange = (val) => {
+const onRepeatEachVerseChange = (val: number) => {
   ...
 };
-const onDelayMultiplierChange = (val) => {
+const onDelayMultiplierChange = (val: number) => {
   ...
 };

As per coding guidelines (TypeScript strictness).

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 78d8006 and f798459.

📒 Files selected for processing (2)
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (7 hunks)
  • src/redux/types/AudioState.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
**/*.*

📄 CodeRabbit inference engine (.cursor/rules/bug-handling-with-todo-comments.mdc)

If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.

**/*.*: Utilize Early Returns: Use early returns to avoid nested conditions and improve readability.
Conditional Classes: Prefer conditional classes over ternary operators for class attributes.

**/*.*: Use comments sparingly, and when you do, make them meaningful.
Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
Use comments to convey the 'why' behind specific actions or explain unusual behavior and potential pitfalls.
Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

**/*.*: Write short functions that only do one thing.
Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.

**/*.*: Always add helpful comments to the code explaining what you are doing.
Never delete old comments, unless they are no longer relevant because the code has been rewritten or deleted.

**/*.*: Choose names for variables, functions, and classes that reflect their purpose and behavior.
A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use specific names that provide a clearer understanding of what the variables represent and how they are used.

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/next-js-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Rely on Next.js Pages Router for state changes.
Minimize 'use client' usage: Prefer server components and Next.js SSR features.
Minimize 'use client' usage: Use 'use client' only for Web API access in small components.
Minimize 'use client' usage: Avoid using 'use client' for data fetching or state management.

**/*.{js,jsx,ts,tsx}: Optimize Web Vitals (LCP, CLS, FID)
Use dynamic loading for non-critical components using @src/components/dls/Spinner/Spinner.tsx

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/{store,redux}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement Redux Toolkit for efficient Redux development for medium-complex state logic

Files:

  • src/redux/types/AudioState.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Use xstate for complex state logic

**/*.{ts,tsx}: TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions
Error handling: define and use custom error types; use async/await correctly; show user-friendly error messages
Use Redux Toolkit for state management and XState for complex state machines
Implement i18n with next-translate in components and utilities

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/redux/**/*

📄 CodeRabbit inference engine (.cursor/rules/redux-folder-structure.mdc)

Follow this folder structure: src/redux/actions/, src/redux/slices/, src/redux/RootState.ts, src/redux/store.ts, src/redux/migrations.ts

Files:

  • src/redux/types/AudioState.ts
src/redux/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/redux-toolkit-best-practices.mdc)

src/redux/**/*.ts: Use Redux Toolkit for efficient Redux development.
Implement slice pattern for organizing Redux code.
Use selectors for accessing state in components.
Use Redux hooks (useSelector, useDispatch) in components.
Follow Redux style guide for naming conventions

Use Redux Toolkit for state management and persistence (redux-persist) within the redux layer

Files:

  • src/redux/types/AudioState.ts
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/types/**/*.{ts,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/types/**/*.{ts,d.ts}: Export types and interfaces from dedicated type files when shared
Place shared types in a types directory

Files:

  • src/redux/types/AudioState.ts
{src,types}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript throughout the codebase with strict configuration

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
{src,types}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use path aliases @/ for src and @/dls/* for design system imports

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Order imports: React first, then external, then internal; alphabetize within groups with blank lines between groups
Naming: camelCase for variables/functions; PascalCase for types, interfaces, and React components; UPPER_CASE for constants
Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx,js,jsx,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: single quotes, 2-space indentation, 100-character line width, and trailing commas

Files:

  • src/redux/types/AudioState.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-functional-components.mdc)

src/components/**/*.tsx: Always use React functional components with hooks.
Use React.FC for functional components with props.

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.{tsx,jsx}: Use functional components over class components
Keep components small and focused
Extract reusable logic into custom hooks
Use composition over inheritance
Split large components into smaller, focused ones
Follow the Rules of Hooks
Use custom hooks for reusable logic
Use appropriate dependency arrays in useEffect
Implement cleanup in useEffect when needed
Avoid nested hooks
Use useState for local component state
Avoid prop drilling through proper state management
Implement proper memoization (useMemo, useCallback)
Use React.memo for expensive components
Avoid unnecessary re-renders
Implement proper lazy loading
Use proper key props in lists
Profile and optimize render performance
Show appropriate loading and error states
Handle async errors properly
Show user-friendly error messages
Implement proper fallback UI
Log errors appropriately
Handle edge cases gracefully
Use semantic HTML elements
Implement proper ARIA attributes
Ensure keyboard navigation
Handle focus management
Provide proper alt text for images
Use proper imports/exports
Document complex component logic

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement proper prop types with TypeScript

**/*.tsx: Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Implement proper error boundaries

React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer React functional components for UI implementation

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Organize React components by feature and place DLS components under src/components/dls

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
🧬 Code graph analysis (1)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (2)
src/utils/verse.ts (3)
  • getChapterNumberFromKey (44-45)
  • makeVerseKey (217-227)
  • getVerseNumberFromKey (54-55)
src/redux/slices/AudioPlayer/state.ts (1)
  • selectAudioPlayerState (12-12)
🔇 Additional comments (3)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (3)

172-209: Range normalization looks solid. LGTM.


347-383: Memo default builder looks correct and dependency list is comprehensive. LGTM.


294-315: Selector usage looks correct; actor context reads are guarded. LGTM.

Comment on lines 1 to 7
export interface RepeatSettingsPreference {
from?: string;
to?: string;
repeatRange?: number | string | null;
repeatEachVerse?: number | string | null;
delayMultiplier?: number | string | 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 | 🟡 Minor

Clarify verse key format and allowed string values.

Add brief docs stating from/to use the chapter:verse format (e.g., "2:255") and that strings for numeric fields may include the literal "Infinity" from persisted JSON. This improves DX and prevents misuse.

TODO: Document the verse key shape and why string unions are needed (JSON persistence of numbers and "Infinity"). As per coding guidelines.

🤖 Prompt for AI Agents
In src/redux/types/AudioState.ts around lines 1-7, the RepeatSettingsPreference
interface lacks documentation for the shape of verse keys and allowed string
values for numeric fields; add JSDoc comments above the interface and for the
from/to fields noting they use "chapter:verse" format (e.g., "2:255"), and for
repeatRange, repeatEachVerse, and delayMultiplier document that they accept
number | string | null and that persisted JSON may supply numeric strings
including the literal "Infinity"; update the field comments to exactly list the
allowed formats/values so consumers and linters understand the string union
usage.

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: 1

Caution

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

⚠️ Outside diff range comments (6)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (4)

525-533: Fix logging key typo.

Use the correct event name.

-    logValueChange('repitition_mode', repetitionMode, mode);
+    logValueChange('repetition_mode', repetitionMode, mode);

535-538: Log the previous verse key, not the repeat count.

Old value should be the prior selected verse key.

-  const onSingleVerseChange = (verseKey) => {
-    logValueChange('repeat_single_verse', verseRepetition.repeatRange, verseKey);
+  const onSingleVerseChange = (verseKey: string) => {
+    logValueChange('repeat_single_verse', verseRepetition.from, verseKey);
     setVerseRepetition({ ...verseRepetition, from: verseKey, to: verseKey });
   };

540-569: Tighten handler types.

Add explicit param types for clarity and safety.

-  const onRangeChange = (range) => {
+  const onRangeChange = (range: { from?: string; to?: string }) => {
@@
-  const onRepeatRangeChange = (val) => {
+  const onRepeatRangeChange = (val: number) => {
@@
-  const onRepeatEachVerseChange = (val) => {
+  const onRepeatEachVerseChange = (val: number) => {
@@
-  const onDelayMultiplierChange = (val) => {
+  const onDelayMultiplierChange = (val: number) => {

358-364: Annotate component with React.FC and props.

Aligns with project guideline to use React.FC for components with props.

-const RepeatAudioModal = ({
+const RepeatAudioModal: React.FC<RepeatAudioModalProps> = ({
   chapterId,
   isOpen,
   onClose,
   defaultRepetitionMode,
   selectedVerseKey,
 }: RepeatAudioModalProps) => {
src/utils/auth/preferencesMapper.ts (1)

38-47: Include repeatSettings in AUDIO group preference mapping to prevent data loss.

The code currently excludes repeatSettings from the AUDIO_PLAYER_STATE preference mapping (lines 38–47 of src/utils/auth/preferencesMapper.ts), even though it's defined in AudioState and managed in Redux. If the server uses a full replace (not merge) strategy for preference groups, a complete sync via stateToPreferenceGroups would clear user repeat settings. Additionally, serialization logic for handling Infinity values exists only in RepeatAudioModal and should be extracted to avoid duplication.

Suggested fix:

  • Add repeatSettings extraction and normalize Infinity values for API compatibility (as shown in the review's diff)
  • Extract the serialization logic from RepeatAudioModal into a shared utility to prevent duplication across preferencesMapper.ts and repeat settings persistence
src/redux/slices/AudioPlayer/state.ts (1)

62-78: REHYDRATE handler doesn't fix Infinity→null; both branches return identical state.

The code checks if repeatRange === null but returns the same spread in both branches, making the condition ineffective. All three numeric fields in repeatSettings are affected by JSON's Infinity→null conversion (per type definition in src/redux/types/AudioState.ts), yet only repeatRange is mentioned in the comment and no actual conversion occurs.

    builder.addCase(REHYDRATE, (state, action) => {
      // @ts-ignore redux-persist types
      const { key, payload } = action;
      if (!payload) return state;
      // Support both root-shaped and slice-shaped payloads
      const slicePayload =
        payload[SliceName.AUDIO_PLAYER_STATE] ?? payload;
      if (!slicePayload) return state;
      const fixNull = (v: unknown) => (v === null ? 'Infinity' : v);
      const nextRepeat =
        slicePayload.repeatSettings
          ? {
              ...slicePayload.repeatSettings,
              repeatRange: fixNull(slicePayload.repeatSettings.repeatRange),
              repeatEachVerse: fixNull(slicePayload.repeatSettings.repeatEachVerse),
              delayMultiplier: fixNull(slicePayload.repeatSettings.delayMultiplier),
            }
          : undefined;
      return {
        ...state,
        ...slicePayload,
        ...(nextRepeat && { repeatSettings: nextRepeat }),
      };
    });
♻️ Duplicate comments (2)
src/redux/types/AudioState.ts (2)

3-9: Document verse key format and JSON-safe numeric strings.

Add concise JSDoc so callers know from/to shape and why string "Infinity" is allowed.

 export interface RepeatSettingsPreference {
-  from?: string;
-  to?: string;
+  /**
+   * Verse keys use "chapter:verse" (e.g., "2:255"). Ranges are not accepted here.
+   */
+  from?: string;
+  /**
+   * Verse keys use "chapter:verse" (e.g., "2:257").
+   */
+  to?: string;
   repeatRange?: number | JsonNumberString | null;
   repeatEachVerse?: number | JsonNumberString | null;
   delayMultiplier?: number | JsonNumberString | null;
 }

11-16: Prefer interface for object shape.

Use an interface for AudioState to align with project guidelines and improve declaration merging flexibility.

-export type AudioState = {
+export interface AudioState {
   enableAutoScrolling: boolean;
   isDownloadingAudio: boolean;
   showTooltipWhenPlayingAudio: boolean;
   repeatSettings?: RepeatSettingsPreference;
-};
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f798459 and 5e4156f.

📒 Files selected for processing (7)
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (6 hunks)
  • src/hooks/auth/usePersistPreferenceGroup.ts (2 hunks)
  • src/redux/defaultSettings/defaultSettings.ts (1 hunks)
  • src/redux/defaultSettings/util.ts (1 hunks)
  • src/redux/slices/AudioPlayer/state.ts (3 hunks)
  • src/redux/types/AudioState.ts (1 hunks)
  • src/utils/auth/preferencesMapper.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (17)
**/*.*

📄 CodeRabbit inference engine (.cursor/rules/bug-handling-with-todo-comments.mdc)

If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.

**/*.*: Utilize Early Returns: Use early returns to avoid nested conditions and improve readability.
Conditional Classes: Prefer conditional classes over ternary operators for class attributes.

**/*.*: Use comments sparingly, and when you do, make them meaningful.
Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
Use comments to convey the 'why' behind specific actions or explain unusual behavior and potential pitfalls.
Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

**/*.*: Write short functions that only do one thing.
Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.

**/*.*: Always add helpful comments to the code explaining what you are doing.
Never delete old comments, unless they are no longer relevant because the code has been rewritten or deleted.

**/*.*: Choose names for variables, functions, and classes that reflect their purpose and behavior.
A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use specific names that provide a clearer understanding of what the variables represent and how they are used.

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/next-js-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Rely on Next.js Pages Router for state changes.
Minimize 'use client' usage: Prefer server components and Next.js SSR features.
Minimize 'use client' usage: Use 'use client' only for Web API access in small components.
Minimize 'use client' usage: Avoid using 'use client' for data fetching or state management.

**/*.{js,jsx,ts,tsx}: Optimize Web Vitals (LCP, CLS, FID)
Use dynamic loading for non-critical components using @src/components/dls/Spinner/Spinner.tsx

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/{store,redux}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement Redux Toolkit for efficient Redux development for medium-complex state logic

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Use xstate for complex state logic

**/*.{ts,tsx}: TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions
Error handling: define and use custom error types; use async/await correctly; show user-friendly error messages
Use Redux Toolkit for state management and XState for complex state machines
Implement i18n with next-translate in components and utilities

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
src/redux/**/*

📄 CodeRabbit inference engine (.cursor/rules/redux-folder-structure.mdc)

Follow this folder structure: src/redux/actions/, src/redux/slices/, src/redux/RootState.ts, src/redux/store.ts, src/redux/migrations.ts

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
src/redux/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/redux-toolkit-best-practices.mdc)

src/redux/**/*.ts: Use Redux Toolkit for efficient Redux development.
Implement slice pattern for organizing Redux code.
Use selectors for accessing state in components.
Use Redux hooks (useSelector, useDispatch) in components.
Follow Redux style guide for naming conventions

Use Redux Toolkit for state management and persistence (redux-persist) within the redux layer

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
{src,types}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript throughout the codebase with strict configuration

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
{src,types}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use path aliases @/ for src and @/dls/* for design system imports

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Order imports: React first, then external, then internal; alphabetize within groups with blank lines between groups
Naming: camelCase for variables/functions; PascalCase for types, interfaces, and React components; UPPER_CASE for constants
Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
**/*.{ts,tsx,js,jsx,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: single quotes, 2-space indentation, 100-character line width, and trailing commas

Files:

  • src/redux/slices/AudioPlayer/state.ts
  • src/hooks/auth/usePersistPreferenceGroup.ts
  • src/utils/auth/preferencesMapper.ts
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
  • src/redux/defaultSettings/defaultSettings.ts
  • src/redux/defaultSettings/util.ts
  • src/redux/types/AudioState.ts
src/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-functional-components.mdc)

src/components/**/*.tsx: Always use React functional components with hooks.
Use React.FC for functional components with props.

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.{tsx,jsx}: Use functional components over class components
Keep components small and focused
Extract reusable logic into custom hooks
Use composition over inheritance
Split large components into smaller, focused ones
Follow the Rules of Hooks
Use custom hooks for reusable logic
Use appropriate dependency arrays in useEffect
Implement cleanup in useEffect when needed
Avoid nested hooks
Use useState for local component state
Avoid prop drilling through proper state management
Implement proper memoization (useMemo, useCallback)
Use React.memo for expensive components
Avoid unnecessary re-renders
Implement proper lazy loading
Use proper key props in lists
Profile and optimize render performance
Show appropriate loading and error states
Handle async errors properly
Show user-friendly error messages
Implement proper fallback UI
Log errors appropriately
Handle edge cases gracefully
Use semantic HTML elements
Implement proper ARIA attributes
Ensure keyboard navigation
Handle focus management
Provide proper alt text for images
Use proper imports/exports
Document complex component logic

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement proper prop types with TypeScript

**/*.tsx: Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Implement proper error boundaries

React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer React functional components for UI implementation

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Organize React components by feature and place DLS components under src/components/dls

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/types/**/*.{ts,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/types/**/*.{ts,d.ts}: Export types and interfaces from dedicated type files when shared
Place shared types in a types directory

Files:

  • src/redux/types/AudioState.ts
🧠 Learnings (1)
📚 Learning: 2025-10-07T08:38:28.343Z
Learnt from: CR
PR: quran/quran.com-frontend-next#0
File: AGENTS.md:0-0
Timestamp: 2025-10-07T08:38:28.343Z
Learning: Applies to **/*.{ts,tsx} : TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions

Applied to files:

  • src/redux/types/AudioState.ts
🧬 Code graph analysis (2)
src/redux/slices/AudioPlayer/state.ts (1)
src/redux/types/AudioState.ts (1)
  • RepeatSettingsPreference (3-9)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (4)
src/utils/verse.ts (3)
  • getChapterNumberFromKey (44-45)
  • makeVerseKey (217-227)
  • getVerseNumberFromKey (54-55)
src/redux/types/AudioState.ts (2)
  • JsonNumberString (1-1)
  • RepeatSettingsPreference (3-9)
src/redux/slices/AudioPlayer/state.ts (1)
  • selectAudioPlayerState (12-12)
src/utils/eventLogger.ts (1)
  • logButtonClick (44-46)
🔇 Additional comments (10)
src/hooks/auth/usePersistPreferenceGroup.ts (2)

166-168: LGTM! Consistency improvement for non-logged-in users.

This change ensures that successCallback is invoked for non-logged-in users, mirroring the behavior for logged-in users (lines 134-136). This fixes a potential bug where callbacks would not be executed when users aren't authenticated.


223-225: LGTM! Consistent callback handling added.

This change ensures that successCallback is invoked for non-logged-in users after dispatching the action, matching the behavior for logged-in users (lines 191-193). Together with the similar change in onXstateSettingsChange, this provides consistent callback handling across all authentication states.

src/redux/defaultSettings/util.ts (1)

9-9: LGTM on named import.

Import matches the new named export; no functional changes.

src/redux/defaultSettings/defaultSettings.ts (1)

3-3: LGTM on named import.

Consistent with updated typings; AUDIO_INITIAL_STATE remains valid with optional repeatSettings.

src/redux/slices/AudioPlayer/state.ts (1)

36-39: Good addition: local persistence of repeatSettings.

Reducer cleanly updates state; pairs well with modal’s optimistic dispatch.

src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (4)

137-153: Nice guard on selectedVerseKey.

Validates the selected key belongs to the current chapter before using it.


236-275: Robust JSON-safe serialization.

"Infinity" handling and selective field emission look correct and side‑effect free.


467-493: Runtime guards + numeric send-path are correct.

Early-returns prevent crashes; removed redundant Number(...) casts. Good.


495-513: onSettingsChange signature verified — code is correct.

The call at lines 505–513 matches the hook's signature exactly:

  • key: 'repeatSettings'
  • value: prepareRepeatSettingsForApi(serializableSettings)
  • action: setRepeatSettings(serializableSettings) (Redux action) ✓
  • undoAction: setRepeatSettings(previousRepeatSettings) (Redux action) ✓
  • preferenceGroup: PreferenceGroup.AUDIO
  • successCallback: () => play(normalizedSettings) (optional param) ✓

The implementation properly serializes settings, prepares the API payload, and dispatches both forward and rollback actions with a success callback.

src/utils/auth/preferencesMapper.ts (1)

4-4: No remaining default imports of AudioState exist.

Verification confirms:

  • Line 4 in preferencesMapper.ts correctly imports { AudioState } as a named export
  • No other default imports of AudioState found across the codebase
  • AudioState.ts exports only as named export

Comment on lines +257 to +275
const serializeRepeatSettings = (settings: RepeatSettingsLike): RepeatSettingsPreference => {
const repeatRange = serializeRepeatNumericValue(settings.repeatRange);
const repeatEachVerse = serializeRepeatNumericValue(settings.repeatEachVerse);
const delayMultiplier = serializeRepeatNumericValue(settings.delayMultiplier);

return {
from: settings.from,
to: settings.to,
...(repeatRange !== undefined && { repeatRange }),
...(repeatEachVerse !== undefined && { repeatEachVerse }),
...(delayMultiplier !== undefined && { delayMultiplier }),
};
};

const serializeOptionalRepeatSettings = (
settings?: RepeatSettingsLike,
): RepeatSettingsPreference | undefined =>
settings ? serializeRepeatSettings(settings) : undefined;

Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

De-duplicate serialization logic across layers.

The modal and preferencesMapper both need JSON-safe repeat settings. Extract serialize/prepare utilities to a shared module and reuse.

I can extract a shared serializer under src/utils/audioRepeat/persistence.ts and update both call sites. Want a patch?

Also applies to: 495-513

🤖 Prompt for AI Agents
In src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx around lines
257-275 (and the duplicate logic at 495-513), extract the serialization helpers
into a new shared module src/utils/audioRepeat/persistence.ts and remove the
duplicated implementations here and in the preferencesMapper; implement and
export serializeRepeatSettings and serializeOptionalRepeatSettings from that
module (export any needed types), then replace the local implementations with
imports from the shared module in both the modal and preferencesMapper call
sites, preserving existing behavior and types.

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 (3)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (3)

527-535: Guard against undefined verse keys in mode change.

Lines 531-532 may assign undefined if selectedVerseKey is not provided or if firstVerseKeyInThisChapter/lastVerseKeyInThisChapter are undefined. This could cause issues downstream. Add guards or use the default verse calculation logic.

Apply this diff:

 const onRepetitionModeChange = (mode: RepetitionMode) => {
   logValueChange('repitition_mode', repetitionMode, mode);
+  const singleVerseKey = selectedVerseKey ?? firstVerseKeyInThisChapter ?? makeVerseKey(chapterNumber, 1);
+  const rangeFromKey = firstVerseKeyInThisChapter ?? makeVerseKey(chapterNumber, 1);
+  const rangeToKey = lastVerseKeyInThisChapter ?? makeVerseKey(chapterNumber, 1);
   setVerseRepetition((prevVerseRepetition) => ({
     ...prevVerseRepetition,
-    from: mode === RepetitionMode.Single ? selectedVerseKey : firstVerseKeyInThisChapter,
-    to: mode === RepetitionMode.Single ? selectedVerseKey : lastVerseKeyInThisChapter,
+    from: mode === RepetitionMode.Single ? singleVerseKey : rangeFromKey,
+    to: mode === RepetitionMode.Single ? singleVerseKey : rangeToKey,
   }));
   setRepetitionMode(mode);
 };

542-556: Redundant spread of range object.

Lines 551-555 spread both range and normalizedRange. Since normalizedRange is computed from the candidates (which include range values), spreading range first is redundant—normalizedRange already contains the final from and to values. Simplify:

 const onRangeChange = (range) => {
   const isFrom = !!range.from;
   const logKey = isFrom ? 'repeat_verse_range_from' : 'repeat_verse_range_to';
   const oldValue = isFrom ? verseRepetition.from : verseRepetition.to;
   const newValue = isFrom ? range.from : range.to;
   logValueChange(logKey, oldValue, newValue);
   const candidateFrom = range.from ?? verseRepetition.from;
   const candidateTo = range.to ?? verseRepetition.to;
   const normalizedRange = normalizeVerseRange(candidateFrom, candidateTo);
   setVerseRepetition({
     ...verseRepetition,
-    ...range,
     ...normalizedRange,
   });
 };

1-630: Consider extracting helpers to reduce component size.

The file is 630 lines with many helper functions (normalization, validation, serialization). Consider extracting these to separate utility modules:

  • src/utils/audioRepeat/validation.ts for normalizeRepeatValue, isVerseKeyInChapter, normalizeVerseRange
  • src/utils/audioRepeat/persistence.ts for serialization helpers (if not already extracted)
  • src/utils/audioRepeat/defaults.ts for buildDefaultVerseRepetition, getRepeatKeys, getRepeatCycles

This would improve maintainability and testability while keeping the component focused on UI concerns.

As per coding guidelines (write short functions, single responsibility).

♻️ Duplicate comments (1)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (1)

236-307: Consider whether serialization extraction was completed.

Previous review suggested extracting serialization logic to a shared module (e.g., src/utils/audioRepeat/persistence.ts) to avoid duplication with preferencesMapper. The comment was marked as addressed, but the logic remains inline here. If the duplication was removed elsewhere but kept here intentionally, this is fine. Otherwise, consider completing the extraction for better maintainability.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e4156f and abbd96f.

📒 Files selected for processing (1)
  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (6 hunks)
🧰 Additional context used
📓 Path-based instructions (13)
**/*.*

📄 CodeRabbit inference engine (.cursor/rules/bug-handling-with-todo-comments.mdc)

If you encounter a bug in existing code, or the instructions lead to suboptimal or buggy code, add comments starting with "TODO:" outlining the problems.

**/*.*: Utilize Early Returns: Use early returns to avoid nested conditions and improve readability.
Conditional Classes: Prefer conditional classes over ternary operators for class attributes.

**/*.*: Use comments sparingly, and when you do, make them meaningful.
Don't comment on obvious things. Excessive or unclear comments can clutter the codebase and become outdated.
Use comments to convey the 'why' behind specific actions or explain unusual behavior and potential pitfalls.
Provide meaningful information about the function's behavior and explain unusual behavior and potential pitfalls.

**/*.*: Write short functions that only do one thing.
Follow the single responsibility principle (SRP), which means that a function should have one purpose and perform it effectively.
If a function becomes too long or complex, consider breaking it into smaller, more manageable functions.

Order functions with those that are composing other functions appearing earlier in the file. For example, if you have a menu with multiple buttons, define the menu function above the buttons.

**/*.*: Always add helpful comments to the code explaining what you are doing.
Never delete old comments, unless they are no longer relevant because the code has been rewritten or deleted.

**/*.*: Choose names for variables, functions, and classes that reflect their purpose and behavior.
A name should tell you why it exists, what it does, and how it is used. If a name requires a comment, then the name does not reveal its intent.
Use specific names that provide a clearer understanding of what the variables represent and how they are used.

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/next-js-conventions.mdc)

**/*.{js,jsx,ts,tsx}: Rely on Next.js Pages Router for state changes.
Minimize 'use client' usage: Prefer server components and Next.js SSR features.
Minimize 'use client' usage: Use 'use client' only for Web API access in small components.
Minimize 'use client' usage: Avoid using 'use client' for data fetching or state management.

**/*.{js,jsx,ts,tsx}: Optimize Web Vitals (LCP, CLS, FID)
Use dynamic loading for non-critical components using @src/components/dls/Spinner/Spinner.tsx

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/components/**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react-functional-components.mdc)

src/components/**/*.tsx: Always use React functional components with hooks.
Use React.FC for functional components with props.

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

**/*.{tsx,jsx}: Use functional components over class components
Keep components small and focused
Extract reusable logic into custom hooks
Use composition over inheritance
Split large components into smaller, focused ones
Follow the Rules of Hooks
Use custom hooks for reusable logic
Use appropriate dependency arrays in useEffect
Implement cleanup in useEffect when needed
Avoid nested hooks
Use useState for local component state
Avoid prop drilling through proper state management
Implement proper memoization (useMemo, useCallback)
Use React.memo for expensive components
Avoid unnecessary re-renders
Implement proper lazy loading
Use proper key props in lists
Profile and optimize render performance
Show appropriate loading and error states
Handle async errors properly
Show user-friendly error messages
Implement proper fallback UI
Log errors appropriately
Handle edge cases gracefully
Use semantic HTML elements
Implement proper ARIA attributes
Ensure keyboard navigation
Handle focus management
Provide proper alt text for images
Use proper imports/exports
Document complex component logic

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.tsx

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Implement proper prop types with TypeScript

**/*.tsx: Prefix interfaces for React props with 'Props' (e.g., ButtonProps)
Implement proper error boundaries

React: use functional components only; extract reusable logic into custom hooks; apply proper memoization (React.memo/useMemo/useCallback)

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/react.mdc)

Use xstate for complex state logic

**/*.{ts,tsx}: TypeScript: prefer interfaces over type aliases for object shapes; avoid any; add explicit return types for public functions
Error handling: define and use custom error types; use async/await correctly; show user-friendly error messages
Use Redux Toolkit for state management and XState for complex state machines
Implement i18n with next-translate in components and utilities

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/typescript.mdc)

**/*.{ts,tsx,d.ts}: Prefer interfaces over types for object definitions
Use type for unions, intersections, and mapped types
Avoid using any, prefer unknown for unknown types
Leverage TypeScript's built-in utility types
Use generics for reusable type patterns
Use PascalCase for type names and interfaces
Use camelCase for variables and functions
Use UPPER_CASE for constants
Use descriptive names with auxiliary verbs (e.g., isLoading, hasError)
Use explicit return types for public functions
Use arrow functions for callbacks and methods
Implement proper error handling with custom error types
Use function overloads for complex type scenarios
Prefer async/await over Promises
Use readonly for immutable properties
Leverage discriminated unions for type safety
Use type guards for runtime type checking
Implement proper null checking
Avoid type assertions unless necessary
Create custom error types for domain-specific errors
Use Result types for operations that can fail
Use try-catch blocks with typed catch clauses
Handle Promise rejections properly

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
{src,types}/**/*.ts?(x)

📄 CodeRabbit inference engine (CLAUDE.md)

Use TypeScript throughout the codebase with strict configuration

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/**/*.tsx

📄 CodeRabbit inference engine (CLAUDE.md)

Prefer React functional components for UI implementation

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
{src,types}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Use path aliases @/ for src and @/dls/* for design system imports

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
src/components/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Organize React components by feature and place DLS components under src/components/dls

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx}: Order imports: React first, then external, then internal; alphabetize within groups with blank lines between groups
Naming: camelCase for variables/functions; PascalCase for types, interfaces, and React components; UPPER_CASE for constants
Functions should be <= 30 lines, prefer early returns, single responsibility, and descriptive names

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
**/*.{ts,tsx,js,jsx,scss}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier formatting: single quotes, 2-space indentation, 100-character line width, and trailing commas

Files:

  • src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx
🧬 Code graph analysis (1)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (3)
src/utils/verse.ts (3)
  • getChapterNumberFromKey (44-45)
  • makeVerseKey (217-227)
  • getVerseNumberFromKey (54-55)
src/redux/types/AudioState.ts (2)
  • JsonNumberString (1-1)
  • RepeatSettingsPreference (3-9)
src/redux/slices/AudioPlayer/state.ts (1)
  • selectAudioPlayerState (12-12)
🔇 Additional comments (4)
src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx (4)

4-6: LGTM: Clean selector aliasing.

The import aliasing for useSelector from both libraries is a clean solution to distinguish XState and Redux selectors throughout the component.


309-358: Well-structured default builder.

The function cleanly delegates to helper functions and follows single responsibility principle. The large input interface is justified given the need to aggregate defaults from multiple sources (selection, actor, persisted).


420-459: Large dependency array is justified.

The useMemo has 17 dependencies, which is substantial but necessary given the complex fallback logic that considers selected verse, actor state, and persisted preferences. This is acceptable given the component's requirements, though it suggests high complexity.

Consider whether the component could be split to reduce complexity, but this is not a blocking issue.


469-495: Robust validation guards implemented.

The function correctly:

  • Normalizes the verse range before processing
  • Guards against undefined from/to (lines 473-475)
  • Validates against NaN values (lines 481-483)
  • Removes redundant Number() wrappers since helpers already return numbers

As per previous feedback (now addressed).

Comment on lines 52 to 63
const normalizeRepeatValue = (
value: number | string | null | undefined,
fallback: number,
): number => {
if (value === null || value === undefined) return fallback;
if (value === Infinity || value === 'Infinity') return Infinity;
if (typeof value === 'string') {
const parsedValue = Number(value);
return Number.isNaN(parsedValue) ? fallback : parsedValue;
}
return value;
};
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 | 🟡 Minor

Validate against negative values for repeat counters.

The function parses numeric strings without validating they're non-negative. Repeat counters should be positive, but a string like "-5" would be parsed and returned. Add a guard to ensure the result is non-negative for repeat settings.

Apply this diff:

 const normalizeRepeatValue = (
   value: number | string | null | undefined,
   fallback: number,
 ): number => {
   if (value === null || value === undefined) return fallback;
   if (value === Infinity || value === 'Infinity') return Infinity;
   if (typeof value === 'string') {
     const parsedValue = Number(value);
-    return Number.isNaN(parsedValue) ? fallback : parsedValue;
+    return Number.isNaN(parsedValue) || parsedValue < 0 ? fallback : parsedValue;
   }
-  return value;
+  return value < 0 ? fallback : value;
 };
🤖 Prompt for AI Agents
In src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx around lines
52 to 63, the normalizeRepeatValue function parses numeric strings but doesn't
guard against negative repeat counts; update it so parsed string values and
numeric inputs that are negative return the fallback instead of a negative
number. Specifically, after parsing a string, check if parsedValue is NaN or
parsedValue < 0 and return fallback in those cases; likewise for the numeric
branch ensure the returned value is non-negative (return fallback if value < 0).
Keep existing handling for null/undefined and 'Infinity'/Infinity.

Comment on lines 497 to 515
const onPlayClick = () => {
logButtonClick('start_repeat_play');
onSettingsChangeWithoutDispatch('repeatSettings', verseRepetition, PreferenceGroup.AUDIO, play);
const normalizedRange = normalizeVerseRange(verseRepetition.from, verseRepetition.to);
const normalizedSettings = {
...verseRepetition,
...normalizedRange,
};
setVerseRepetition(normalizedSettings);
const serializableSettings = serializeRepeatSettings(normalizedSettings);
const previousRepeatSettings = serializeOptionalRepeatSettings(persistedRepeatSettings);
onSettingsChange(
'repeatSettings',
prepareRepeatSettingsForApi(serializableSettings),
setRepeatSettings(serializableSettings),
setRepeatSettings(previousRepeatSettings),
PreferenceGroup.AUDIO,
() => play(normalizedSettings),
);
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Redux persistence appears implemented but verify dispatch.

Line 510 passes setRepeatSettings(serializableSettings) to onSettingsChange, which should dispatch the action to Redux. This addresses the previous comment about Redux not being updated. However, verify that onSettingsChange actually dispatches the action (it should based on the signature, but confirmation would be helpful).

Additionally, this function handles multiple responsibilities (normalize → update state → serialize → persist → play). Consider extracting some logic:

const prepareAndSerializeSettings = (settings: VerseRepetitionState) => {
  const normalizedRange = normalizeVerseRange(settings.from, settings.to);
  const normalizedSettings = { ...settings, ...normalizedRange };
  return {
    normalizedSettings,
    serializable: serializeRepeatSettings(normalizedSettings),
  };
};

const onPlayClick = () => {
  logButtonClick('start_repeat_play');
  const { normalizedSettings, serializable } = prepareAndSerializeSettings(verseRepetition);
  setVerseRepetition(normalizedSettings);
  // ... rest of persistence logic
};
🤖 Prompt for AI Agents
In src/components/AudioPlayer/RepeatAudioModal/RepeatAudioModal.tsx around lines
497 to 515, verify that onSettingsChange actually dispatches the Redux action
(ensure you pass the action creator or call dispatch inside onSettingsChange
rather than passing the result of a setter), and refactor the
normalize/serialize sequence into a helper (e.g., prepareAndSerializeSettings)
that returns normalizedSettings and serializable settings; then simplify
onPlayClick to call that helper, update local state with
setVerseRepetition(normalizedSettings), and call onSettingsChange with the
proper action/dispatch arguments followed by the play callback.

@zonetecde zonetecde changed the title QF-2639 & QF-QF-239 - Fix RepeatAudioModal to correctly load settings QF-2639 & QF-239 - Fix RepeatAudioModal to correctly load settings Nov 5, 2025
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