Skip to content

fix: condense watch queries #7548

Merged
tataihono merged 46 commits intomainfrom
25-00-NC-fix-condense-watch-queries
Aug 29, 2025
Merged

fix: condense watch queries #7548
tataihono merged 46 commits intomainfrom
25-00-NC-fix-condense-watch-queries

Conversation

@Kneesal
Copy link
Member

@Kneesal Kneesal commented Aug 27, 2025

Summary by CodeRabbit

  • New Features

    • Audio languages now delivered as id/slug pairs per variant.
    • New hook-based subtitle loader enabling on-demand subtitle fetches with loading state.
  • Bug Fixes

    • Avoids offering unavailable audio/subtitle options.
    • More reliable enabling/disabling of subtitle tracks during playback.
  • Refactor

    • Watch state and APIs standardized to use subtitle IDs and audio id/slug pairs.
  • Tests

    • Test suites and fixtures updated for the new ID/slug data shapes.

tataihono and others added 19 commits August 25, 2025 01:13
…ove middleware handling

- Added Upstash Redis integration to cache audio language variants for videos.
- Enhanced middleware to handle audio language redirects based on user preferences.
- Updated package dependencies for Redis and Zod.
- Removed unused components and queries related to audio language selection.
- Improved error handling for Redis cache and API fetch scenarios.
- Updated Redis cache implementation to use `set` with expiration options.
- Refactored middleware tests to align with new Redis methods.
- Improved error handling and response headers in API for variant languages.
- Added validation for missing language keys in static props.
…e-functionality' of https://github.com/JesusFilm/core into tataihono/wat-151-add-comprehensive-tests-for-middleware-functionality
- Refactored redisClient function to reuse Redis instance for improved performance.
- Added condition to create a new Redis client only in test environment or if not already initialized.
- Added `setHeader` mock function to the API response object in variantLanguages tests for improved test coverage and accuracy.
…tPage.spec.tsx

- Eliminated the test case that checked for redirection based on cookie language, as it was deemed unnecessary for current test coverage.
- Changed Redis URL and token environment variables to use KV REST API instead of Redis URL and token.
- Ensured compatibility with the new configuration for improved functionality.
- Changed the structure of variantLanguages from an array to an object for improved data handling.
- Updated middleware and API response to accommodate the new object format.
- Enhanced validation and error handling for missing language IDs and slugs in the API.
- Adjusted tests to reflect the new variantLanguages structure and ensure accurate response handling.
…rection

- Replaced Redis `get` and `set` methods with `exists`, `hset`, and `hget` for improved data management.
- Enhanced middleware logic to handle Redis cache checks and API fetches more efficiently.
- Updated tests to reflect changes in Redis interactions and ensure accurate behavior for audio language redirection.
- Improved error handling for Redis operations and validation of cached data.
…dling

- Introduced AUDIO_LANGUAGE_REDIRECT_CACHE_SCHEMA_VERSION for better cache key management in middleware.
- Updated Redis cache key structure to include schema version for audio language redirection.
- Simplified language handling by removing unnecessary prefixes in variantLanguages.
- Enhanced error logging for Redis operations to improve debugging and maintainability.
- Adjusted tests to reflect changes in middleware logic and Redis interactions.
…e redirection

- Consolidated matcher configuration by merging multiple objects into a single array for cleaner code.
- Maintained existing functionality while improving readability and maintainability of the middleware logic.
…meter

- Added logic in middleware to skip audio language redirection when the r=0 query parameter is present.
- Updated tests to verify that redirection is correctly bypassed under this condition.
- Modified VideoCard component to include r=0 in generated links for consistent behavior across the application.
- Introduced a new TUI configuration option in nx.json to manage feature toggling.
- Updated the AudioTrackSelect component to improve helper text handling and language selection logic.
- Removed unused audio language setter utility and its associated tests to streamline the codebase.
@Kneesal Kneesal requested a review from tataihono August 27, 2025 22:29
@Kneesal Kneesal self-assigned this Aug 27, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Walkthrough

Refactors watch app language handling: audio languages flattened to {id, slug}, subtitles represented as languageId string arrays, GET_LANGUAGES_SLUG replaced by GET_VARIANT_LANGUAGES_ID_AND_SLUG, subtitleUpdate replaced by a new useSubtitleUpdate hook, and corresponding context, pages, components, and tests updated to the new shapes.

Changes

Cohort / File(s) Summary
Watch pages: static props refactor
apps/watch/pages/watch/[part1]/[part2].tsx, apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
Replace GET_LANGUAGES_SLUG with GET_VARIANT_LANGUAGES_ID_AND_SLUG; map variantLanguages → AudioLanguageData[] ({id, slug}) and subtitles → string[] (languageId); update getStaticProps props/types and page component prop names.
Watch context and initialization
apps/watch/src/libs/watchContext/WatchContext.tsx, apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts, apps/watch/src/libs/watchContext/TestWatchState.tsx
Add/export AudioLanguageData; rename state/actions to videoAudioLanguagesIdsAndSlugs and videoSubtitleLanguageIds; update reducer, actions, initialization, exports and test display values to new shapes.
Language switch dialog & utils
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx, apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.tsx, apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts
Remove dynamic GET_LANGUAGES_SLUG fetch; consume videoAudioLanguagesIdsAndSlugs and videoSubtitleLanguageIds; adapt selection, loading and helperText logic; update current-audio matching to flat AudioLanguageData.
Subtitle update → hook migration
apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.tsx, .../useSubtitleUpdate/index.ts, apps/watch/src/libs/watchContext/subtitleUpdate/index.ts
Add useSubtitleUpdate hook exposing subtitleUpdate and subtitlesLoading (uses lazy GET_SUBTITLES); re-export useSubtitleUpdate; remove old subtitleUpdate re-export. Deleted legacy subtitleUpdate implementation file.
Hero/Player subtitle consumers
apps/watch/src/components/NewVideoContentPage/VideoContentHero/HeroVideo/HeroVideo.tsx, apps/watch/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoPlayer.tsx
Replace direct subtitleUpdate import with useSubtitleUpdate hook; remove videoSubtitleLanguages from payloads and deps; call subtitleUpdate({ player, subtitleLanguage, subtitleOn, autoSubtitle }).
NewVideoContentPage wiring
apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
Dispatch SetVideoAudioLanguages with mapped AudioLanguageData[] (id, slug) as videoAudioLanguagesIdsAndSlugs.
Variant languages hook & mocks
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts, .../index.ts, .../useVariantLanguagesIdAndSlugQuery.mock.ts, .../useVariantLanguagesIdAndSlugQuery.spec.tsx
Add GET_VARIANT_LANGUAGES_ID_AND_SLUG query and useVariantLanguagesIdAndSlugQuery hook; add index re-exports; provide typed mocks and spec for full/empty/partial responses.
Subtitle hook tests & test data
apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.spec.tsx, .../useSubtitleUpdate.mock.ts, apps/watch/src/components/SubtitleDialog/testData.ts, apps/watch/src/components/NewVideoContentPage/VideoContentHero/VideoContentHero.spec.tsx
Add tests/mocks for useSubtitleUpdate; move GET_SUBTITLES import to shared hook module; tests wrapped with MockedProvider; provide mock subtitle/video fixtures.
Specs updated to new shapes
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx, apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.spec.tsx, apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts, apps/watch/src/libs/watchContext/WatchContext.spec.tsx, apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts, apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx
Update fixtures and assertions to use videoAudioLanguagesIdsAndSlugs ({id,slug}) and videoSubtitleLanguageIds (string[]); align currentAudioLanguage to {id,slug}; remove tests asserting GET_LANGUAGES_SLUG fetch.
Test/UI import & minor cleanups
various spec files (examples: .../AudioLanguageSelect.spec.tsx, .../AudioLanguageSelectContent.spec.tsx, .../VideoContentPage.spec.tsx, .../LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx)
Clean unused imports, remove MockedResponse usages, simplify render destructuring and test helpers to match refactor.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Component as UI Component (Player/Hero)
  participant Hook as useSubtitleUpdate
  participant Apollo as Apollo Client
  participant GQL as GraphQL API
  participant Player as Video.js Player

  User->>Component: Change subtitle settings
  Component->>Hook: subtitleUpdate({ player, subtitleLanguage, subtitleOn, autoSubtitle })
  Hook->>Player: Collect textTracks()
  alt subtitle off or no language
    Hook->>Player: Disable all subtitle tracks
  else Fetch and show
    Hook->>Apollo: useLazyQuery(GET_SUBTITLES, { id: variant.slug })
    Apollo->>GQL: GET_SUBTITLES
    GQL-->>Apollo: subtitles data
    Apollo-->>Hook: data
    Hook->>Player: addRemoteTextTrack(..., default=true)
    Hook->>Player: Toggle tracks (show selected, disable others)
  end
Loading
sequenceDiagram
  autonumber
  participant Next as Next.js getStaticProps
  participant Apollo as Apollo Client
  participant GQL as GraphQL API
  participant Page as Page component

  Next->>Apollo: GET_VARIANT_LANGUAGES_ID_AND_SLUG({ id })
  Apollo->>GQL: Query
  GQL-->>Apollo: { variantLanguages: [{id, slug}], subtitles: [{languageId}] }
  Apollo-->>Next: Data
  Next-->>Next: Map -> audio: [{id, slug}], subtitles: [languageId]
  Next-->>Page: props { videoAudioLanguagesIdsAndSlugs, videoSubtitleLanguageIds }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 25-00-NC-fix-condense-watch-queries

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

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

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbit help to get the list of available commands.

Other keywords and placeholders

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

CodeRabbit Configuration File (.coderabbit.yaml)

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

Status, Documentation and Community

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

@nx-cloud
Copy link

nx-cloud bot commented Aug 27, 2025

View your CI Pipeline Execution ↗ for commit 9f38696

Command Status Duration Result
nx run watch-e2e:e2e ✅ Succeeded 11s View ↗
nx run-many --target=vercel-alias --projects=watch ✅ Succeeded 4s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 13s View ↗
nx run-many --target=deploy --projects=watch ✅ Succeeded 1m 55s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-28 23:35:22 UTC

@nx-cloud
Copy link

nx-cloud bot commented Aug 27, 2025

View your CI Pipeline Execution ↗ for commit 207dafb

Command Status Duration Result
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-27 22:30:47 UTC

1 similar comment
@nx-cloud
Copy link

nx-cloud bot commented Aug 27, 2025

View your CI Pipeline Execution ↗ for commit 207dafb

Command Status Duration Result
nx run-many --target=prisma-generate --all --pa... ✅ Succeeded 3s View ↗

☁️ Nx Cloud last updated this comment at 2025-08-27 22:30:47 UTC

@github-actions
Copy link
Contributor

github-actions bot commented Aug 27, 2025

The latest updates on your projects.

Name Status Preview Updated (UTC)
watch ✅ Ready watch preview Fri Aug 29 11:32:26 NZST 2025

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 (5)
apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts (2)

126-137: Preserve query/hash and normalize slug when updating URL.

Current replacement can drop ?query/#hash and mis-compare when “.html” or query is present.

-function updateURLIfNeeded({ router, targetSlug }: UpdateURLParams): void {
-  if (!targetSlug) return
-
-  const path = router.asPath.split('/')
-  const currentPathSlug = last(path)?.replace('.html', '')
-
-  if (currentPathSlug !== targetSlug) {
-    void router.push(
-      router.asPath.replace(last(router.asPath.split('/')) ?? '', targetSlug)
-    )
-  }
-}
+function updateURLIfNeeded({ router, targetSlug }: UpdateURLParams): void {
+  if (!targetSlug) return
+  const asPath = router.asPath
+  const qIdx = asPath.indexOf('?')
+  const hIdx = asPath.indexOf('#')
+  const cut = qIdx === -1 ? (hIdx === -1 ? asPath.length : hIdx) : (hIdx === -1 ? qIdx : Math.min(qIdx, hIdx))
+  const pathname = asPath.slice(0, cut)
+  const suffix = asPath.slice(cut) // keeps ? and/or #
+
+  const segments = pathname.split('/')
+  const currentSegment = segments[segments.length - 1]
+  const currentPathSlug = currentSegment?.replace('.html', '')
+
+  if (currentPathSlug !== targetSlug) {
+    segments[segments.length - 1] = targetSlug
+    void router.push(segments.join('/') + suffix)
+  }
+}

148-165: Simplify helper text logic and handle null slug.

Avoid double set and show a sensible fallback when slug is null.

-function setHelper({
-  isPreferred,
-  preferredLanguage,
-  setHelperText,
-  t
-}: SetLanguageAndHelperParams): void {
-  setHelperText(t('2000 translations'))
-
-  if (isPreferred) {
-    setHelperText(t('2000 translations'))
-  } else if (preferredLanguage) {
-    setHelperText(
-      t('Not available in {{value}}', {
-        value: preferredLanguage.slug
-      })
-    )
-  }
-}
+function setHelper({
+  isPreferred,
+  preferredLanguage,
+  setHelperText,
+  t
+}: SetLanguageAndHelperParams): void {
+  if (isPreferred) {
+    setHelperText(t('2000 translations'))
+    return
+  }
+  if (preferredLanguage) {
+    const value = preferredLanguage.slug ?? preferredLanguage.name
+    setHelperText(t('Not available in {{value}}', { value }))
+    return
+  }
+  setHelperText(t('2000 translations'))
+}
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

1-1: Remove unused GraphQL imports.

Leftovers from the old fetch path cause lint noise.

-import { useLazyQuery } from '@apollo/client'
@@
-import { GetLanguagesSlug } from '../../../../__generated__/GetLanguagesSlug'
@@
-import { GET_LANGUAGES_SLUG } from '../../../libs/useLanguagesSlugQuery'

Also applies to: 14-16

apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts (1)

43-56: Harden path parsing to handle query/hash/trailing slash and only strip a terminal .html

routerAsPath can include query strings or hashes and sometimes ends with a trailing slash, which will break slug detection. Also, replace('.html', '') may strip mid-path occurrences. Tighten this logic.

Apply:

-  const currentLanguageSlug = routerAsPath
-    .split('/')
-    .pop()
-    ?.replace('.html', '')
+  const cleanPath = routerAsPath.split('?')[0].split('#')[0]
+  const currentLanguageSlug = cleanPath
+    .split('/')
+    .filter(Boolean)
+    .pop()
+    ?.replace(/\.html$/i, '')
+  // if encoded, decode it for a fair comparison
+  const decodedSlug = currentLanguageSlug != null
+    ? decodeURIComponent(currentLanguageSlug)
+    : undefined

and update the finder:

-  const selectedLanguage = allLanguages.find(
-    (lang) => lang.slug === currentLanguageSlug
-  )
+  const selectedLanguage = allLanguages.find(
+    (lang) => lang.slug === decodedSlug
+  )
apps/watch/src/libs/watchContext/WatchContext.tsx (1)

240-258: Fix crash in reducer when videoAudioLanguagesIdsAndSlugs is undefined (unit test failure)

Static tests show “TypeError: videoAudioLanguagesIdsAndSlugs is not iterable”. Default to an empty array before iterating.

Apply:

-    case 'SetVideoAudioLanguages': {
-      const videoAudioLanguagesIdsAndSlugs =
-        action.videoAudioLanguagesIdsAndSlugs
+    case 'SetVideoAudioLanguages': {
+      const videoAudioLanguagesIdsAndSlugs =
+        action.videoAudioLanguagesIdsAndSlugs ?? []

Optionally, pull siteLanguageSlugs out of the loop:

-      for (const lang of videoAudioLanguagesIdsAndSlugs) {
-        const siteLanguageMapping = LANGUAGE_MAPPINGS[state.siteLanguage]
-        const siteLanguageSlugs = siteLanguageMapping?.languageSlugs || []
+      const siteLanguageSlugs =
+        LANGUAGE_MAPPINGS[state.siteLanguage]?.languageSlugs ?? []
+      for (const lang of videoAudioLanguagesIdsAndSlugs) {
🧹 Nitpick comments (11)
apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx (1)

231-238: Prefer MUI Button and add accessible labelling per apps/ guidelines.*

Replace raw with MUI’s Button, add aria-label.

-                <button
-                  onClick={() => setShowShare(true)}
-                  className="inline-flex items-center gap-2 px-4 py-2 rounded-full text-gray-900 font-bold uppercase tracking-wider bg-white hover:bg-[#cb333b] hover:text-white transition-colors duration-200 text-sm cursor-pointer"
-                >
-                  <LinkExternal className="w-4 h-4" />
-                  {t('Share')}
-                </button>
+                <Button
+                  aria-label={t('Share this video')}
+                  color="inherit"
+                  size="small"
+                  variant="contained"
+                  onClick={() => setShowShare(true)}
+                  startIcon={<LinkExternal className="w-4 h-4" />}
+                >
+                  {t('Share')}
+                </Button>

Add at top:

import { Button } from '@mui/material'

apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts (1)

200-205: Align selection with actual availability for the current video.

selectLanguageForVideo doesn’t know which variant languages are available; yet the caller gates on videoAudioLanguagesIdsAndSlugs. Pass that list in and verify currentAudioLanguage.id is available before using it.

 export interface AudioLanguageSetterParams {
   /** Current audio language selected by the system based on availability */
   currentAudioLanguage?: VariantLanguageIdAndSlug
+  /** Languages available for the current video (id+slug only) */
+  availableVariantLanguages?: VariantLanguageIdAndSlug[]
@@
 export function selectLanguageForVideo({
   currentAudioLanguage,
+  availableVariantLanguages,
   allLanguages,
   audioLanguage,
   router,
   setHelperText,
   t
 }: AudioLanguageSetterParams): void {
@@
-  if (
-    currentAudioLanguage != null &&
-    preferredAudioLanguage != null &&
-    currentAudioLanguage.id === preferredAudioLanguage.id
-  ) {
+  const isCurrentAvailable =
+    currentAudioLanguage != null &&
+    availableVariantLanguages?.some((v) => v.id === currentAudioLanguage.id)
+
+  if (
+    isCurrentAvailable &&
+    preferredAudioLanguage != null &&
+    currentAudioLanguage!.id === preferredAudioLanguage.id
+  ) {
@@
-  else if (currentAudioLanguage != null) {
+  else if (isCurrentAvailable && currentAudioLanguage != null) {

Update the caller (AudioTrackSelect) to pass availableVariantLanguages={videoAudioLanguagesIdsAndSlugs}.

I can push a follow-up commit wiring this through if you want.

Also applies to: 206-218, 220-233

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

56-73: Tighten effect gating and fix duplicate dependency.

  • Gate in one place; no need to check loading twice.
  • Remove duplicated loading in the deps array.
   useEffect(() => {
     // Run automatic selection logic based on current state
-    if (allLanguages == null || loading) return
+    if (allLanguages == null || loading) return
 
     const params = {
       currentAudioLanguage,
+      // pass when you adopt the optional refactor in audioLanguageSetter.ts
+      // availableVariantLanguages: videoAudioLanguagesIdsAndSlugs,
       allLanguages,
       audioLanguage,
       router,
       setHelperText,
       t
     }
 
-    if (videoId != null) {
-      if (videoAudioLanguagesIdsAndSlugs == null || loading) return
-      selectLanguageForVideo(params)
-    }
+    if (videoId != null && videoAudioLanguagesIdsAndSlugs != null) {
+      selectLanguageForVideo(params)
+    }
   }, [
     loading,
     allLanguages,
     audioLanguage,
     currentAudioLanguage,
     t,
     router,
     videoId,
-    videoAudioLanguagesIdsAndSlugs,
-    loading
+    videoAudioLanguagesIdsAndSlugs
   ])

Also applies to: 81-83

apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts (1)

19-26: Minor: default parameter can simplify nil coalescing.

-export function initializeVideoLanguages(
-  dispatch: Dispatch<WatchAction>,
-  videoAudioLanguagesIdsAndSlugs: AudioLanguageData[],
-  videoSubtitleLanguages: SubtitleLanguage[]
-): void {
+export function initializeVideoLanguages(
+  dispatch: Dispatch<WatchAction>,
+  videoAudioLanguagesIdsAndSlugs: AudioLanguageData[] = [],
+  videoSubtitleLanguages: SubtitleLanguage[] = []
+): void {
@@
   dispatch({
     type: 'SetVideoAudioLanguages',
-    videoAudioLanguagesIdsAndSlugs: videoAudioLanguagesIdsAndSlugs ?? []
+    videoAudioLanguagesIdsAndSlugs
   })
@@
   dispatch({
     type: 'SetVideoSubtitleLanguages',
-    videoSubtitleLanguages: videoSubtitleLanguages ?? []
+    videoSubtitleLanguages
   })
apps/watch/src/libs/watchContext/WatchContext.tsx (2)

270-289: Clarify autoSubtitle semantics to a strict boolean

Using undefined for “off” can complicate consumers. Prefer an explicit boolean.

Apply:

-      return {
-        ...state,
-        videoSubtitleLanguages,
-        autoSubtitle: subtitleAvailable === false ? undefined : true
-      }
+      return {
+        ...state,
+        videoSubtitleLanguages,
+        autoSubtitle: subtitleAvailable === true
+      }

If undefined is relied upon elsewhere, feel free to skip; otherwise this simplifies downstream checks.


387-397: Effect dependencies: consider stabilizing on the object or memoized values

Accessing nested props directly in deps arrays can be brittle if initialState identity changes. If feasible, depend on initialState or memoize the two arrays before passing to initializeVideoLanguages.

Example:

-  }, [
-    initialState.videoSubtitleLanguages,
-    initialState.videoAudioLanguagesIdsAndSlugs
-  ])
+  }, [initialState])

Only if this doesn’t fight your ESLint rules; otherwise keep as-is.

apps/watch/pages/watch/[part1]/[part2].tsx (3)

56-65: Avoid defining a query inside a page that other pages import from

[part3].tsx imports GET_VARIANT_LANGUAGES_ID_AND_SLUG from this page. Importing from a page couples build chunks and can create surprising bundles. Move the query to a shared module (e.g., src/libs/queries/getVariantLanguagesIdAndSlug.ts) and import it from both pages.

Proposed new shared file:

// src/libs/queries/getVariantLanguagesIdAndSlug.ts
import { gql } from '@apollo/client'

export const GET_VARIANT_LANGUAGES_ID_AND_SLUG = gql`
  query GetVariantLanguagesIdAndSlug($id: ID!) {
    video(id: $id, idType: databaseId) {
      variantLanguages {
        id
        slug
      }
    }
  }
`

Then update both pages to import from that path.


9-13: Remove stale imports for GetLanguagesSlug/GET_LANGUAGES_SLUG

These appear unused after the refactor and can be dropped to reduce bundle size and noise.

Apply:

-import {
-  GetLanguagesSlug,
-  GetLanguagesSlugVariables,
-  GetLanguagesSlug_video_variantLanguagesWithSlug as VideoAudioLanguage
-} from '../../../__generated__/GetLanguagesSlug'
-...
-import { GET_LANGUAGES_SLUG } from '../../../src/libs/useLanguagesSlugQuery'

Also applies to: 25-26


175-193: Prefer nullish coalescing over logical OR when defaulting arrays

Use ?? [] so empty arrays don’t get overridden (unlikely with .map, but keeps consistency with the other page).

-      videoAudioLanguagesData =
-        data?.video?.variantLanguages?.map(
+      videoAudioLanguagesData =
+        data?.video?.variantLanguages?.map(
           ({ id, slug }): AudioLanguageData => ({
             id,
             slug
           })
-        ) || []
+        ) ?? []
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (2)

44-53: Don’t import queries from sibling page files

Mirror the suggestion in [part2].tsx: import GET_VARIANT_LANGUAGES_ID_AND_SLUG from a shared module, not ../[part2].

Apply:

-import { GET_VARIANT_LANGUAGES_ID_AND_SLUG } from '../[part2]'
+import { GET_VARIANT_LANGUAGES_ID_AND_SLUG } from '../../../../src/libs/queries/getVariantLanguagesIdAndSlug'

11-15: Remove stale and unused imports

GetLanguagesSlug/GET_LANGUAGES_SLUG are unused now; the VariantLanguageIdAndSlug alias also appears unused. Clean them up.

Apply:

-import {
-  GetLanguagesSlug,
-  GetLanguagesSlugVariables,
-  GetLanguagesSlug_video_variantLanguagesWithSlug as VideoAudioLanguage
-} from '../../../../__generated__/GetLanguagesSlug'
-...
-import { GET_LANGUAGES_SLUG } from '../../../../src/libs/useLanguagesSlugQuery'
-...
-import {
-  GetVariantLanguagesIdAndSlug,
-  GetVariantLanguagesIdAndSlug_video_variantLanguages as VariantLanguageIdAndSlug,
-  GetVariantLanguagesIdAndSlugVariables
-} from '../../../../__generated__/GetVariantLanguagesIdAndSlug'
+import {
+  GetVariantLanguagesIdAndSlug,
+  GetVariantLanguagesIdAndSlugVariables
+} from '../../../../__generated__/GetVariantLanguagesIdAndSlug'

Also applies to: 31-31, 50-52

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 40d8191 and 207dafb.

⛔ Files ignored due to path filters (1)
  • apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts is excluded by !**/__generated__/**
📒 Files selected for processing (9)
  • apps/watch/pages/watch/[part1]/[part2].tsx (6 hunks)
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (5 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (4 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts (2 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts (2 hunks)
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx (1 hunks)
  • apps/watch/src/libs/watchContext/TestWatchState.tsx (1 hunks)
  • apps/watch/src/libs/watchContext/WatchContext.tsx (7 hunks)
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts (2 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/src/libs/watchContext/TestWatchState.tsx
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/src/libs/watchContext/TestWatchState.tsx
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/src/libs/watchContext/TestWatchState.tsx
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/audioLanguageSetter/audioLanguageSetter.ts
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts
🧬 Code graph analysis (5)
apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts (1)
apps/watch/src/libs/watchContext/WatchContext.tsx (2)
  • WatchAction (170-182)
  • AudioLanguageData (18-21)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
apps/watch/src/libs/localeMapping/localeMapping.ts (1)
  • LANGUAGE_MAPPINGS (22-204)
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (3)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (18-21)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (21-23)
  • GetVariantLanguagesIdAndSlugVariables (25-27)
apps/watch/pages/watch/[part1]/[part2].tsx (1)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (56-65)
apps/watch/pages/watch/[part1]/[part2].tsx (3)
apps/watch/__generated__/VideoContentFields.ts (1)
  • VideoContentFields (97-118)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (18-21)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (21-23)
  • GetVariantLanguagesIdAndSlugVariables (25-27)
apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts (2)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (18-21)
libs/shared/ui/src/components/LanguageAutocomplete/LanguageAutocomplete.tsx (1)
  • LanguageOption (33-38)
🪛 GitHub Check: test (22, 1/3)
apps/watch/src/libs/watchContext/WatchContext.tsx

[failure] 247-247: WatchContext › reducer › SetVideoAudioLanguages › should set currentAudioLanguage to undefined when no match found
TypeError: videoAudioLanguagesIdsAndSlugs is not iterable

at videoAudioLanguagesIdsAndSlugs (src/libs/watchContext/WatchContext.tsx:247:26)
at Object.<anonymous> (src/libs/watchContext/WatchContext.spec.tsx:148:31)

[failure] 247-247: WatchContext › reducer › SetVideoAudioLanguages › should set video audio languages and select matching currentAudioLanguage
TypeError: videoAudioLanguagesIdsAndSlugs is not iterable

at videoAudioLanguagesIdsAndSlugs (src/libs/watchContext/WatchContext.tsx:247:26)
at Object.<anonymous> (src/libs/watchContext/WatchContext.spec.tsx:115:31)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Deploy Preview (watch, 7548/merge, pull_request, 22)
  • GitHub Check: test (22, 2/3)
  • GitHub Check: build (22)
  • GitHub Check: lint (22)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (6)
apps/watch/src/libs/watchContext/TestWatchState.tsx (2)

22-24: LGTM: migrated to videoAudioLanguagesIdsAndSlugs.

Display aligns with new AudioLanguageData shape.


30-31: LGTM: currentAudioLanguage now references id directly.

Matches flattened state.

apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.ts (1)

3-5: LGTM: aligns with new AudioLanguageData shape and action payload.

No functional concerns.

Also applies to: 17-33

apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts (1)

24-35: Good migration to flat AudioLanguageData; matching by id is correct

The switch to AudioLanguageData and id-based match is consistent with the new context shape and avoids nested lookups. LGTM.

apps/watch/pages/watch/[part1]/[part2].tsx (1)

37-45: Aligning types looks good; ensure alias imports aren’t left unused

The AudioLanguageData adoption and prop rename are consistent. Double-check that the alias VariantLanguageIdAndSlug is either used or removed to keep TS happy with noUnusedLocals.

apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (1)

180-198: Consistent defaulting and mapping LGTM

Using ?? [] here avoids the empty-array false positive. Mapping directly to AudioLanguageData is clean.

- Consolidated loading state checks for audio languages and video ID.
- Enhanced optional chaining for current audio language.
- Added fallback for current language display.
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 (2)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (2)

1-1: Remove unused imports to satisfy ESLint and trim bundle.

These are no longer used after the refactor.

Apply this diff:

-import { useLazyQuery } from '@apollo/client'
@@
-import { ReactElement, memo, useEffect, useMemo, useState } from 'react'
+import { ReactElement, memo, useMemo } from 'react'
@@
-import { GetLanguagesSlug } from '../../../../__generated__/GetLanguagesSlug'
-import { GET_LANGUAGES_SLUG } from '../../../libs/useLanguagesSlugQuery'

Also applies to: 5-5, 14-15


110-143: Use MUI components instead of raw HTML per apps/ guidelines.*

Replace div/label/span+classes with MUI Box/Stack/FormLabel/Typography.

Apply this diff:

-  return (
-    <div className="mx-6 font-sans">
-      <div className="flex items-center justify-between">
-        <label
-          htmlFor="audio-select"
-          className="block text-xl font-medium text-gray-700 ml-7"
-        >
-          {t('Language')}
-        </label>
-        <span className="text-sm text-gray-400 opacity-60">
-          {currentLanguage?.nativeName}
-        </span>
-      </div>
-      <div className="relative mt-1 flex items-start gap-2">
-        <div className="pt-4">
-          <SpatialAudioOffOutlinedIcon fontSize="small" />
-        </div>
-        <div className="relative w-full">
-          <LanguageAutocomplete
-            value={{
-              id: currentLanguage?.id ?? '',
-              localName: currentLanguage?.localName,
-              nativeName: currentLanguage?.nativeName,
-              slug: currentLanguage?.slug
-            }}
-            onChange={handleChange}
-            languages={languages}
-            loading={loading}
-            disabled={loading}
-            renderInput={renderInput(helperText)}
-            renderOption={renderOption}
-          />
-        </div>
-      </div>
-    </div>
-  )
+  return (
+    <Box sx={{ mx: 6, fontFamily: 'sans-serif' }}>
+      <Stack direction="row" alignItems="center" justifyContent="space-between">
+        <FormLabel htmlFor="audio-select" sx={{ typography: 'h6', ml: 7 }}>
+          {t('Language')}
+        </FormLabel>
+        <Typography variant="body2" color="text.secondary" sx={{ opacity: 0.6 }}>
+          {currentLanguage?.nativeName}
+        </Typography>
+      </Stack>
+      <Stack direction="row" spacing={2} alignItems="flex-start" mt={1}>
+        <Box pt={4}>
+          <SpatialAudioOffOutlinedIcon fontSize="small" />
+        </Box>
+        <Box sx={{ position: 'relative', width: '100%' }}>
+          <LanguageAutocomplete
+            value={{
+              id: currentLanguage?.id ?? '',
+              localName: currentLanguage?.localName,
+              nativeName: currentLanguage?.nativeName,
+              slug: currentLanguage?.slug
+            }}
+            onChange={handleChange}
+            languages={languages}
+            loading={loading}
+            disabled={loading}
+            renderInput={renderInput(helperText)}
+            renderOption={renderOption}
+          />
+        </Box>
+      </Stack>
+    </Box>
+  )

Add imports:

+import { Box, Stack, FormLabel, Typography } from '@mui/material'
♻️ Duplicate comments (1)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

89-96: Fix availability check to match the message language selection.

Compare against currentLanguage?.id (the language referenced in the message), not currentAudioLanguage?.id. Also drop currentAudioLanguage from the deps if no longer used.

Apply this diff:

-    const found = videoAudioLanguagesIdsAndSlugs?.find(
-      ({ id }) => id === currentAudioLanguage?.id
-    )
+    const found = videoAudioLanguagesIdsAndSlugs?.find(
+      ({ id }) => id === currentLanguage?.id
+    )
@@
-    currentAudioLanguage,
     videoAudioLanguagesIdsAndSlugs,

Also applies to: 103-107

🧹 Nitpick comments (6)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

29-41: Remove unused dispatch.

It’s destructured but not used; cleans up ESLint warning.

Apply this diff:

   const {
     state: {
       allLanguages,
       currentAudioLanguage,
       audioLanguage,
       videoId,
       videoAudioLanguagesIdsAndSlugs,
       loading
-    },
-    dispatch
+    }
   } = useWatch()
apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts (4)

84-87: DRY and type the currentAudioLanguage shape

Define a small typed helper and use it here to avoid repeating object literals and to satisfy the “define a type if possible” guideline.

Apply:

+// helper for currentAudioLanguage objects
+type AudioRef = { id: string; slug: string }
+const audio = (id: string, slug: string): AudioRef => ({ id, slug })

And replace inline literal:

-        currentAudioLanguage: { id: '496', slug: 'spanish' },
+        currentAudioLanguage: audio('496', 'spanish'),

186-191: Reuse the helper for readability

Use the proposed audio() helper here too.

-        currentAudioLanguage: { id: '529', slug: 'english' },
+        currentAudioLanguage: audio('529', 'english'),

210-215: Reuse the helper for readability

Same here—replace inline literal with audio().

-        currentAudioLanguage: { id: '529', slug: 'english' },
+        currentAudioLanguage: audio('529', 'english'),

156-159: Add test for unmatched currentAudioLanguage fallback
Under getCurrentAudioLanguage.spec.ts (e.g. after the “Priority order” tests), add a case where currentAudioLanguage is provided but its id/slug aren’t in mockLanguages to lock in the existing priority logic (path slug → audioLanguage). For example:

it('should ignore unknown currentAudioLanguage and fall back to path slug', () => {
  const result = getCurrentAudioLanguage({
    allLanguages: mockLanguages,
    currentAudioLanguage: { id: '9999', slug: 'does-not-exist' },
    routerAsPath: '/watch/jesus/spanish.html',
    audioLanguage: '529',
  })
  expect(result).toEqual(expectedSpanishOption)
})
apps/watch/src/libs/watchContext/WatchContext.spec.tsx (1)

175-175: Nit: test title typo

Remove stray closing parenthesis.

-it('should not set autoSubtitle when language preference is met langPrefMet is true)', () => {
+it('should not set autoSubtitle when language preference is met (langPrefMet is true)', () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 8456ab4 and bf7b8d8.

📒 Files selected for processing (5)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (5 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts (4 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts (2 hunks)
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx (0 hunks)
  • apps/watch/src/libs/watchContext/WatchContext.spec.tsx (10 hunks)
💤 Files with no reviewable changes (1)
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts
  • apps/watch/src/libs/watchContext/WatchContext.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts
  • apps/watch/src/libs/watchContext/WatchContext.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/src/components/LanguageSwitchDialog/utils/getCurrentAudioLanguage/getCurrentAudioLanguage.spec.ts
  • apps/watch/src/libs/watchContext/WatchContext.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-15T03:19:49.322Z
Learnt from: Kneesal
PR: JesusFilm/core#7171
File: apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.spec.tsx:447-451
Timestamp: 2025-07-15T03:19:49.322Z
Learning: In the SubtitlesSelect component tests, the team intentionally uses integration tests that verify interactions work without errors rather than explicitly testing function calls, with explanatory comments to inform future developers of this testing strategy.

Applied to files:

  • apps/watch/src/libs/watchContext/WatchContext.spec.tsx
🧬 Code graph analysis (1)
apps/watch/src/libs/watchContext/WatchContext.spec.tsx (1)
apps/watch/src/libs/watchContext/WatchContext.tsx (2)
  • WatchAction (169-181)
  • reducer (223-357)
🪛 GitHub Actions: autofix.ci
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx

[warning] 1-1: ESLint: 'useLazyQuery' is defined but never used. (no-unused-vars)


[warning] 5-5: ESLint: 'useEffect' is defined but never used. (no-unused-vars)


[warning] 5-5: ESLint: 'useState' is defined but never used. (no-unused-vars)


[warning] 14-14: ESLint: 'GetLanguagesSlug' is defined but never used. (no-unused-vars)


[warning] 15-15: ESLint: 'GET_LANGUAGES_SLUG' is defined but never used. (no-unused-vars)


[warning] 39-39: ESLint: 'dispatch' is assigned a value but never used. (no-unused-vars)

🔇 Additional comments (15)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (5)

36-37: LGTM: correct state source (ids+slugs) is used.

Switching to videoAudioLanguagesIdsAndSlugs aligns with the new data shape.


67-72: LGTM: reload gating logic is correct.

Computing reload only when InstantSearch is absent and the selected language exists matches the intent.


85-99: LGTM: helperText early returns are clear.

The loading and no-video branches are succinct and readable.


112-116: Verify label-input association for accessibility.

Ensure the underlying input rendered by LanguageAutocomplete actually has id="audio-select"; otherwise the FormLabel/htmlFor (or label/htmlFor) won’t bind.

If LanguageAutocomplete supports it, pass an input id prop (e.g., inputId="audio-select"). If not, update renderInput to set id on the TextField, or add aria-labelledby linking to the label.

Also applies to: 127-140


136-138: LGTM: loading/disabled wiring.

The loading state is correctly applied to UX and interactivity.

apps/watch/src/libs/watchContext/WatchContext.spec.tsx (10)

94-99: Good: verifies id/slug shape and selection of currentAudioLanguage

Assertions align with reducer semantics and new payload shape.

Also applies to: 101-106, 108-114


142-147: Good: subtitle IDs shape and autoSubtitle=true when available

Matches reducer branch where subtitleAvailable === true.

Also applies to: 149-155, 151-153


158-169: Good: autoSubtitle disabled when preference not met and subtitle not available

Expectation of undefined aligns with reducer.

Also applies to: 170-173


176-186: Good: respects langPrefMet early return (no autoSubtitle change)

Accurately captures the reducer’s early-return behavior.

Also applies to: 187-198, 194-196


200-216: Good: autoSubtitle=true when preference not met and subtitle available

Covers the positive path.

Also applies to: 218-224, 220-221


226-242: Good: autoSubtitle undefined when preference not met and subtitle unavailable

Covers the negative path.

Also applies to: 244-250, 246-247


252-266: Good: handles undefined currentAudioLanguage

Confirms fallback behavior without currentAudioLanguage.

Also applies to: 267-273, 269-270


467-469: Good: asserts new initial arrays are empty

New state shape validated.


480-487: Good: initial state with extras wires through and selects currentAudioLanguage

Matches reducer’s selection logic and preserved fields.

Also applies to: 490-492, 513-516


520-533: The reducer will set currentAudioLanguage whenever the siteLanguage’s slugs include the audio track’s slug. Since defaultInitialState.siteLanguage defaults to "en", and "english" is one of its slugs, your test with siteLanguage "en" and an English track will deterministically pick up that audio as the current audio, making currentAudioLanguage non-undefined. To force the fallback path you’re testing (no audio match → currentAudioLanguage stays undefined), inject a siteLanguage whose slug list doesn’t include "english" (e.g. "fr").

No further changes required—your diff is correct.

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 (4)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (3)

1369-1390: Migrate empty array case to new key

This still uses videoAudioLanguages: []. Switch to videoAudioLanguagesIdsAndSlugs: [].

-        videoAudioLanguages: []
+        videoAudioLanguagesIdsAndSlugs: []

1031-1031: Await userEvent calls to avoid flakiness

userEvent is async; not awaiting can cause racey assertions.

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))
-      userEvent.click(screen.getByRole('option', { name: 'Spanish' }))
+      await userEvent.click(screen.getByRole('option', { name: 'Spanish' }))

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))
-      userEvent.click(screen.getByRole('option', { name: 'French' }))
+      await userEvent.click(screen.getByRole('option', { name: 'French' }))

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))
-      userEvent.click(screen.getByRole('option', { name: 'English' }))
+      await userEvent.click(screen.getByRole('option', { name: 'English' }))

-      userEvent.click(screen.getByRole('combobox'))
+      await userEvent.click(screen.getByRole('combobox'))
-      userEvent.click(screen.getByRole('option', { name: 'English' }))
+      await userEvent.click(screen.getByRole('option', { name: 'English' }))

Also applies to: 1037-1037, 1125-1125, 1131-1131, 1210-1210, 1293-1293, 1352-1352, 1411-1411


513-547: Update all tests to use flattened audio language shapes
Replace legacy videoAudioLanguages and nested currentAudioLanguage in tests (lines 513–547, 1308, 1367, 1389) with the new props:

-      videoAudioLanguages: [
-        { __typename: 'LanguageWithSlug' as const, slug: 'spanish', language: { id: '496', slug: 'spanish', … } }
-      ],
-      currentAudioLanguage: { __typename: 'LanguageWithSlug' as const, slug: 'spanish', language: { id: '496', slug: 'spanish', … } }
+      videoAudioLanguagesIdsAndSlugs: [{ id: '496', slug: 'spanish' }],
+      currentAudioLanguage: { id: '496', slug: 'spanish' }
apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx (1)

170-204: Align GraphQL mock to match GET_LANGUAGES_SLUG selection
In NewVideoContentPage.spec.tsx the getLanguagesSlugMock.result.data.video.variantLanguagesWithSlug items include extra language.slug and language.name fields, but the active GET_LANGUAGES_SLUG query only selects

variantLanguagesWithSlug {
  slug
  language { id }
}

Update the mock to only include

{
  __typename: 'LanguageWithSlug',
  slug: string,
  language: { __typename: 'Language', id: string }
}

so tests reflect the real query shape and avoid false positives.

🧹 Nitpick comments (5)
apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts (1)

21-24: Type and de-duplicate mock audio language objects.

Reduce repetition and satisfy “define a type if possible” by typing/hoisting the fixture.

Within tests, annotate inline or hoist a shared const:

-    const mockAudioLanguages = [
-      {
-        id: '529',
-        slug: 'english'
-      }
-    ]
+    const mockAudioLanguages: Array<{ id: string; slug: string }> = [
+      { id: '529', slug: 'english' }
+    ]

Additionally (outside selected ranges), consider adding once near the top and reusing:

// near the top of `describe`
const englishAudio = { id: '529', slug: 'english' } as const;

Then use [englishAudio] in each test.

Also applies to: 46-49, 71-74, 119-122, 176-179

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (1)

767-771: Prefer userEvent over fireEvent for realistic interactions

Use userEvent.type/click for Autocomplete; also remove unnecessary await on fireEvent.change.

-      fireEvent.click(screen.getByRole('combobox'))
-      fireEvent.change(screen.getByRole('combobox'), {
+      await userEvent.click(screen.getByRole('combobox'))
+      await userEvent.type(screen.getByRole('combobox'), '496')
-      fireEvent.click(screen.getByRole('combobox'))
-      fireEvent.change(screen.getByRole('combobox'), {
+      await userEvent.click(screen.getByRole('combobox'))
+      await userEvent.type(screen.getByRole('combobox'), '496')
-      await fireEvent.change(input, { target: { value: 'Spanish' } })
+      await userEvent.type(input, 'Spanish')
-      fireEvent.click(screen.getByText('Spanish'))
+      await userEvent.click(screen.getByText('Spanish'))

Also applies to: 828-831, 940-948

apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx (3)

25-26: Rename the describe title to match the component

For clarity and test discovery, align the suite name with the component/file name.

-describe('NewContentPage', () => {
+describe('NewVideoContentPage', () => {

149-151: Use jest.spyOn for window.open and restore mocks to avoid leakage

Directly reassigning window.open can leak across tests. Spy + restore is safer.

-  const user = userEvent.setup()
-  window.open = jest.fn()
+  const user = userEvent.setup()
+  const openSpy = jest
+    .spyOn(window, 'open')
+    .mockImplementation(() => null as unknown as Window)

@@
-  expect(window.open).toHaveBeenCalledWith(
+  expect(openSpy).toHaveBeenCalledWith(
     'https://join.bsfinternational.org/?utm_source=jesusfilm-watch',
     '_blank'
   )

Add once per file to auto-restore:

afterEach(() => {
  jest.restoreAllMocks()
})

Also applies to: 164-167


219-229: Consider singular/plural handling for “audio language(s)”

Current expectation reads “1 audio languages”. Prefer singularization at the source (TestWatchState) and reflect it in the test to improve UX consistency.

Example in the source (pseudo):

const label = `${count} audio ${count === 1 ? 'language' : 'languages'}`

Then update this test accordingly:

-  screen.getByText('videoAudioLanguagesIdsAndSlugs: 1 audio languages')
+  screen.getByText('videoAudioLanguagesIdsAndSlugs: 1 audio language')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between bf7b8d8 and 1b4d33d.

📒 Files selected for processing (5)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (8 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (7 hunks)
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx (1 hunks)
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx (1 hunks)
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx
  • apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
🧠 Learnings (1)
📚 Learning: 2025-07-15T03:19:49.322Z
Learnt from: Kneesal
PR: JesusFilm/core#7171
File: apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.spec.tsx:447-451
Timestamp: 2025-07-15T03:19:49.322Z
Learning: In the SubtitlesSelect component tests, the team intentionally uses integration tests that verify interactions work without errors rather than explicitly testing function calls, with explanatory comments to inform future developers of this testing strategy.

Applied to files:

  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Deploy Preview (watch, 7548/merge, pull_request, 22)
  • GitHub Check: test (22, 3/3)
  • GitHub Check: build (22)
  • GitHub Check: test (22, 2/3)
  • GitHub Check: test (22, 1/3)
  • GitHub Check: lint (22)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (17)
apps/watch/src/libs/watchContext/initializeVideoLanguages/initializeVideoLanguages.spec.ts (6)

21-24: Audio payload shape and key rename look correct — LGTM.

Using flat { id, slug } and asserting videoAudioLanguagesIdsAndSlugs matches the refactor intent.

Also applies to: 26-26, 39-39


46-49: Subtitle payload assertion aligns with new API — LGTM.

Asserting videoSubtitleLanguageIds: string[] is correct.

Also applies to: 51-51, 64-64


71-74: Order-sensitive dispatch checks are precise — LGTM.

First audio (videoAudioLanguagesIdsAndSlugs), then subtitles (videoSubtitleLanguageIds).

Also applies to: 76-76, 89-89, 94-94


99-99: Empty audio array case covered — LGTM.

Confirms stable two-dispatch behavior.

Also applies to: 107-107, 112-112


119-122: Empty subtitle array case covered — LGTM.

Also applies to: 130-130, 135-135


146-146: Both empty arrays case covered — LGTM.

Also applies to: 151-151

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (8)

83-83: LGTM: consistent Apollo test harness

Using MockedProvider with addTypename={false} is appropriate for these tests.


265-269: LGTM: migrated to videoAudioLanguagesIdsAndSlugs

Shape { id, slug } aligns with the refactor.


450-459: LGTM: preferred-language available path uses new shapes

Both videoAudioLanguagesIdsAndSlugs and currentAudioLanguage are flattened correctly.


1006-1015: LGTM: availability list migrated

Correct id/slug entries for reload=true scenario.


1100-1109: LGTM: availability list excludes French

Matches reload=false expectation.


1183-1188: LGTM: single-availability case

Undefined in list implies reload=false as asserted.


1262-1271: LGTM: instantSearch present path

Availability list correct; reload=false as expected.


1330-1330: LGTM: undefined availability handled

Covers edge case explicitly.

apps/watch/src/components/NewVideoContentPage/NewVideoContentPage.spec.tsx (3)

219-221: LGTM: assertion updated to new audio-languages key

Expectation now matches the renamed payload key. Looks good.


227-229: LGTM: post-fetch assertion

The updated count assertion reflects the new key correctly.


5-10: Retain legacy GET_LANGUAGES_SLUG imports—still in use
The GET_LANGUAGES_SLUG query and its generated types power NewVideoContentPage.tsx via useLanguagesSlugQuery and remain the current implementation; migrate this spec when you adopt GET_VARIANT_LANGUAGES_ID_AND_SLUG.

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

♻️ Duplicate comments (2)
apps/watch/pages/watch/[part1]/[part2].tsx (2)

159-163: Fix identifier mismatch: passing Relay content.id to a databaseId query will fail.

Either pass content.databaseId or switch the query to idType: slug and pass the variant slug. Prefer aligning with databaseId to avoid ambiguity.

-        variables: {
-          id: contentData.content.id
-        }
+        variables: {
+          id: contentData.content.databaseId
+        }

If databaseId isn’t in VideoContentFields, extend the fragment to include it before applying this change:

fragment VideoContentFields on Video {
  id
  databaseId
  # ...existing fields
}

Verification script:

#!/bin/bash
# Ensure VideoContentFields selects databaseId
rg -nP -C2 'fragment\s+VideoContentFields\b' --type=ts --type=tsx | sed -n '1,200p'
# Confirm no remaining call sites pass `.id` into GET_VARIANT_LANGUAGES_ID_AND_SLUG
rg -nP -C2 'GET_VARIANT_LANGUAGES_ID_AND_SLUG' apps/watch/pages/watch --type=tsx | sed -n '1,200p'

171-173: Ensure videoSubtitleLanguageIds is always an array.

Current assignment can be undefined, violating the declared string[] type and causing runtime surprises.

-      videoSubtitleLanguageIds = data?.video?.subtitles?.map(
-        ({ languageId }) => languageId
-      )
+      videoSubtitleLanguageIds =
+        data?.video?.subtitles?.map(({ languageId }) => languageId) ?? []
🧹 Nitpick comments (5)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx (2)

71-74: Avoid unnecessary act wrapper; wait on the hook result instead of the mock.

This reduces test flakiness and asserts what matters.

-    await act(
-      async () => await waitFor(() => expect(mockResult).toHaveBeenCalled())
-    )
+    await waitFor(() => expect(result.current.data?.video).toEqual(mockVideo))

11-35: Deduplicate fixtures by reusing the exported mocks.

Import from useVariantLanguagesIdAndSlugQuery.mock.ts and reference mockVariantLanguagesIdAndSlugData.video to cut duplication and keep tests in sync with other suites.

apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)

1-1: Optional: adopt TypedDocumentNode to eliminate generics at call sites.

This tightens type inference and reduces boilerplate.

-import { QueryHookOptions, QueryResult, gql, useQuery } from '@apollo/client'
+import { QueryHookOptions, QueryResult, gql, useQuery } from '@apollo/client'
+import type { TypedDocumentNode } from '@graphql-typed-document-node/core'
@@
-export const GET_VARIANT_LANGUAGES_ID_AND_SLUG = gql`
+export const GET_VARIANT_LANGUAGES_ID_AND_SLUG: TypedDocumentNode<
+  GetVariantLanguagesIdAndSlug,
+  GetVariantLanguagesIdAndSlugVariables
+> = gql`

Also applies to: 8-8, 20-20

apps/watch/pages/watch/[part1]/[part2].tsx (1)

164-170: Use nullish coalescing, not logical OR, for array fallbacks.

Avoids surprising fallback on truthy empty arrays (style consistency with Part3 page).

-      videoAudioLanguagesData =
-        data?.video?.variantLanguages?.map(
+      videoAudioLanguagesData =
+        data?.video?.variantLanguages?.map(
           ({ id, slug }): AudioLanguageData => ({
             id,
             slug
-          })
-        ) || []
+          })
+        ) ?? []
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (1)

51-58: Opportunity to truly condense watch queries: inline languages into the content query.

To remove an extra network round-trip in getStaticProps, consider adding variantLanguages { id slug } and subtitles { languageId } to GET_VIDEO_CONTENT_PART_3 (and similarly for Part2), then map directly from content. This keeps the new hook available for client-side needs but avoids the extra SSR fetch.

Also applies to: 170-188

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1b4d33d and 15b08a0.

📒 Files selected for processing (6)
  • apps/watch/pages/watch/[part1]/[part2].tsx (5 hunks)
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (5 hunks)
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/index.ts (1 hunks)
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts (1 hunks)
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx (1 hunks)
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/index.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts
🧬 Code graph analysis (5)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.spec.tsx (1)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (2)
  • useVariantLanguagesIdAndSlugQuery (22-37)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (8-20)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts (1)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (1)
  • GetVariantLanguagesIdAndSlug (27-29)
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (3)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (17-20)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (27-29)
  • GetVariantLanguagesIdAndSlugVariables (31-33)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (8-20)
apps/watch/pages/watch/[part1]/[part2].tsx (3)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (17-20)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (27-29)
  • GetVariantLanguagesIdAndSlugVariables (31-33)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (8-20)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (2)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/index.ts (2)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (3-3)
  • useVariantLanguagesIdAndSlugQuery (2-2)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (27-29)
  • GetVariantLanguagesIdAndSlugVariables (31-33)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Deploy Preview (watch, 7548/merge, pull_request, 22)
  • GitHub Check: lint (22)
🔇 Additional comments (3)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)

22-37: LGTM — thin, typed wrapper around Apollo useQuery.

Hook shape and type plumbing look correct.

apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.mock.ts (1)

3-38: Mocks look solid and typed; good coverage (full/empty/partial).

No changes requested.

apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (1)

186-187: Nice: nullish fallback preserves array type.

Consistent, safe mapping.

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

Caution

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

⚠️ Outside diff range comments (6)
apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx (2)

9-9: Fix ESLint: remove unused mockGetCookie and its import

mockGetCookie is never used, causing the pipeline warning. Drop the alias and the now-unused getCookie import.

Apply this diff:

-import { getCookie } from '../../libs/cookieHandler'
@@
-const mockGetCookie = getCookie as jest.MockedFunction<typeof getCookie>

Also applies to: 32-32


1-167: Remove all remaining legacy language-query usages
The search shows leftover GET_LANGUAGES_SLUG and useLanguagesSlugQuery references under apps/watch/src/libs/useLanguagesSlugQuery/ and in multiple components (e.g. AudioLanguageSelect, NewVideoContentPage, VideoHero.stories). These must be removed or replaced to fully eliminate legacy language queries.

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (2)

74-79: Await user-event calls to fix no-floating-promises errors.

user-event v14+ returns promises; instantiate a user and await interactions.

Apply these diffs:

@@
   beforeEach(() => {
     useRouterMock.mockReturnValue(mockRouter)
     useTranslationMock.mockReturnValue({ t: mockT })
     useInstantSearchMock.mockReturnValue(undefined) // Default to no instant search
     jest.clearAllMocks()
+    user = userEvent.setup()
   })

Add a user variable near the top of the file (after mocks):

+let user: ReturnType<typeof userEvent.setup>

Replace direct calls:

-      userEvent.click(screen.getByRole('combobox'))
+      await user.click(screen.getByRole('combobox'))
@@
-      userEvent.click(screen.getByRole('option', { name: 'Spanish' }))
+      await user.click(screen.getByRole('option', { name: 'Spanish' }))
@@
-      userEvent.click(screen.getByRole('combobox'))
+      await user.click(screen.getByRole('combobox'))
@@
-      userEvent.click(screen.getByRole('option', { name: 'French' }))
+      await user.click(screen.getByRole('option', { name: 'French' }))
@@
-      userEvent.click(screen.getByRole('combobox'))
+      await user.click(screen.getByRole('combobox'))
@@
-      userEvent.click(screen.getByRole('option', { name: 'Spanish' }))
+      await user.click(screen.getByRole('option', { name: 'Spanish' }))
@@
-      userEvent.click(screen.getByRole('combobox'))
+      await user.click(screen.getByRole('combobox'))
@@
-      userEvent.click(screen.getByRole('option', { name: 'English' }))
+      await user.click(screen.getByRole('option', { name: 'English' }))
@@
-      userEvent.click(screen.getByRole('combobox'))
+      await user.click(screen.getByRole('combobox'))
@@
-      userEvent.click(screen.getByRole('option', { name: 'English' }))
+      await user.click(screen.getByRole('option', { name: 'English' }))

Also applies to: 1031-1031, 1037-1037, 1125-1125, 1131-1131, 1204-1204, 1210-1210, 1287-1287, 1293-1293, 1346-1346, 1352-1352, 1405-1405, 1411-1411


1367-1390: Migrate remaining test state to new shape (IdsAndSlugs).

This test still uses legacy videoAudioLanguages: []; use videoAudioLanguagesIdsAndSlugs: [] to align with the public state.

-        ],
-        videoAudioLanguages: []
+        ],
+        videoAudioLanguagesIdsAndSlugs: []
apps/watch/src/libs/watchContext/WatchContext.tsx (2)

196-209: Fix createContext default to enable “used outside provider” guard.

Currently context is created with a non-undefined default, so the guard never triggers. Initialize with undefined and type accordingly.

-const WatchContext = createContext<{
-  state: WatchState
-  dispatch: Dispatch<WatchAction>
-}>({
-  state: {
-    siteLanguage: 'en',
-    audioLanguage: '529',
-    subtitleLanguage: '529',
-    subtitleOn: false,
-    loading: false
-  },
-  dispatch: () => null
-})
+const WatchContext = createContext<
+  { state: WatchState; dispatch: Dispatch<WatchAction> } | undefined
+>(undefined)
@@
-  const context = useContext(WatchContext)
+  const context = useContext(WatchContext)
 
-  if (context === undefined) {
+  if (context === undefined) {
     throw new Error('useWatch must be used within a WatchProvider')
   }
 
   return context

Also applies to: 426-438


237-262: Correct audio-language selection logic; slug comparison is mismatched.

LANGUAGE_MAPPINGS.languageSlugs contains values like 'spanish-latin-american.html', while lang.slug is typically 'spanish'. Comparing them won’t match. Prefer ID-based match (audioLanguage and site language’s languageId), and optionally fall back to videoVariantSlug sans “.html”.

Apply this refactor:

-    case 'SetVideoAudioLanguages': {
-      const videoAudioLanguagesIdsAndSlugs =
-        action.videoAudioLanguagesIdsAndSlugs
-
-      // Find the best matching audio language for the current user preference
-      let currentAudioLanguage: AudioLanguageData | undefined
-
-      // Check if user's audio preference is available for this video
-      for (const lang of videoAudioLanguagesIdsAndSlugs) {
-        const siteLanguageMapping = LANGUAGE_MAPPINGS[state.siteLanguage]
-        const siteLanguageSlugs = siteLanguageMapping?.languageSlugs || []
-
-        if (
-          lang.id === state.audioLanguage ||
-          siteLanguageSlugs.includes(lang.slug || '')
-        ) {
-          currentAudioLanguage = lang
-          break
-        }
-      }
+    case 'SetVideoAudioLanguages': {
+      const videoAudioLanguagesIdsAndSlugs =
+        action.videoAudioLanguagesIdsAndSlugs
+
+      // Prefer explicit user preference; then site language’s canonical ID; then variant slug match
+      const preferredSiteLangId =
+        LANGUAGE_MAPPINGS[state.siteLanguage]?.languageId
+      const variantSlugNoHtml = state.videoVariantSlug?.replace(/\.html$/, '')
+
+      let currentAudioLanguage: AudioLanguageData | undefined =
+        videoAudioLanguagesIdsAndSlugs.find(
+          (l) => l.id === state.audioLanguage
+        ) ??
+        (preferredSiteLangId != null
+          ? videoAudioLanguagesIdsAndSlugs.find((l) => l.id === preferredSiteLangId)
+          : undefined) ??
+        (variantSlugNoHtml != null
+          ? videoAudioLanguagesIdsAndSlugs.find(
+              (l) => (l.slug ?? '') === variantSlugNoHtml
+            )
+          : undefined)
 
       // If no matches found, currentAudioLanguage remains undefined
       return {
         ...state,
         videoAudioLanguagesIdsAndSlugs,
         currentAudioLanguage
       }
     }
♻️ Duplicate comments (1)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

85-101: Fix availability check to compare against currentLanguage, not currentAudioLanguage.

Helper text text uses currentLanguage.* but membership checks against currentAudioLanguage?.id. This diverges when currentAudioLanguage is undefined or differs from the displayed language, producing wrong “Not available in …” messaging.

Apply this diff:

-    const found = videoAudioLanguagesIdsAndSlugs?.find(
-      ({ id }) => id === currentAudioLanguage?.id
-    )
+    const found = videoAudioLanguagesIdsAndSlugs?.find(
+      ({ id }) => id === currentLanguage?.id
+    )
@@
-    currentAudioLanguage,
     videoAudioLanguagesIdsAndSlugs,
 
     currentLanguage
🧹 Nitpick comments (9)
apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx (3)

2-2: Prefer userEvent and async finders for realistic interactions

Switch from fireEvent + synchronous assertions to userEvent + findByRole to reduce flakiness and better emulate user behavior.

Apply this diff:

-import { fireEvent, render, waitFor } from '@testing-library/react'
+import { render } from '@testing-library/react'
+import userEvent from '@testing-library/user-event'
@@
-  it('should render share button', () => {
-    const { getByRole } = render(
+  it('should render share button', async () => {
+    const { getByRole, findByRole } = render(
       <MockedProvider>
         <SnackbarProvider>
           <VideoProvider value={{ content: videos[0] }}>
             <VideoContentPage />
           </VideoProvider>
         </SnackbarProvider>
       </MockedProvider>
     )
     expect(getByRole('button', { name: 'Share' })).toBeInTheDocument()
-    fireEvent.click(getByRole('button', { name: 'Share' }))
-    expect(
-      getByRole('dialog', { name: 'Share this video' })
-    ).toBeInTheDocument()
+    const user = userEvent.setup()
+    await user.click(getByRole('button', { name: 'Share' }))
+    expect(await findByRole('dialog', { name: 'Share this video' })).toBeInTheDocument()
   })
@@
-  it('should render download button', () => {
-    const { getByRole } = render(
+  it('should render download button', async () => {
+    const { getByRole, findByRole } = render(
       <MockedProvider>
         <SnackbarProvider>
           <VideoProvider value={{ content: videos[0] }}>
             <VideoContentPage />
           </VideoProvider>
         </SnackbarProvider>
       </MockedProvider>
     )
     expect(getByRole('button', { name: 'Download' })).toBeInTheDocument()
-    fireEvent.click(getByRole('button', { name: 'Download' }))
-    expect(getByRole('dialog', { name: 'Download Video' })).toBeInTheDocument()
+    const user = userEvent.setup()
+    await user.click(getByRole('button', { name: 'Download' }))
+    expect(await findByRole('dialog', { name: 'Download Video' })).toBeInTheDocument()
   })

Also applies to: 124-138, 140-153


106-114: Simplify async assertion: use findByRole instead of waitFor + getByRole

Cleaner and less timing-sensitive.

Apply this diff:

-  it('should render children', async () => {
-    const { getByRole } = render(
+  it('should render children', async () => {
+    const { findByRole } = render(
       <MockedProvider mocks={[getVideoChildrenMock]}>
         <SnackbarProvider>
           <VideoProvider value={{ content: videos[1] }}>
             <VideoContentPage />
           </VideoProvider>
         </SnackbarProvider>
       </MockedProvider>
     )
 
-    await waitFor(() =>
-      expect(
-        getByRole('heading', { name: videos[1].title[0].value })
-      ).toBeInTheDocument()
-    )
+    expect(
+      await findByRole('heading', { name: videos[1].title[0].value })
+    ).toBeInTheDocument()
   })

Also applies to: 116-121


36-63: Avoid double cast (unknown as) in test data

If TS version allows, prefer satisfies to validate shape without widening, or narrow the object to the fields VideoContentPage actually consumes.

Example (if TS ≥ 4.9):

-  ] as unknown as CoreVideo[]
+  ] satisfies CoreVideo[]

If not, consider defining a minimal test helper type that matches the component’s usage to avoid unknown.

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (2)

27-39: Remove unused dispatch to satisfy ESLint.

dispatch isn’t used; drop it from destructuring.

-  const {
-    state: {
-      allLanguages,
-      currentAudioLanguage,
-      audioLanguage,
-      videoId,
-      videoAudioLanguagesIdsAndSlugs,
-      loading
-    },
-    dispatch
-  } = useWatch()
+  const {
+    state: {
+      allLanguages,
+      currentAudioLanguage,
+      audioLanguage,
+      videoId,
+      videoAudioLanguagesIdsAndSlugs,
+      loading
+    }
+  } = useWatch()

104-139: Follow apps/ guideline: prefer MUI components over raw HTML + utility classes.*

Replace div/label/spans/classes with MUI Box/Stack/Typography/FormLabel for consistency and built-in a11y.

Example refactor:

-  return (
-    <div className="mx-6 font-sans">
-      <div className="flex items-center justify-between">
-        <label
-          htmlFor="audio-select"
-          className="block text-xl font-medium text-gray-700 ml-7"
-        >
-          {t('Language')}
-        </label>
-        <span className="text-sm text-gray-400 opacity-60">
-          {currentLanguage?.nativeName}
-        </span>
-      </div>
-      <div className="relative mt-1 flex items-start gap-2">
-        <div className="pt-4">
-          <SpatialAudioOffOutlinedIcon fontSize="small" />
-        </div>
-        <div className="relative w-full">
+  return (
+    <Box sx={{ mx: 6, fontFamily: 'sans-serif' }}>
+      <Stack direction="row" alignItems="center" justifyContent="space-between">
+        <FormLabel htmlFor="audio-select">
+          <Typography variant="h6">{t('Language')}</Typography>
+        </FormLabel>
+        <Typography variant="caption" color="text.secondary">
+          {currentLanguage?.nativeName}
+        </Typography>
+      </Stack>
+      <Stack direction="row" spacing={2} mt={1} alignItems="flex-start">
+        <Box pt={1}>
+          <SpatialAudioOffOutlinedIcon fontSize="small" />
+        </Box>
+        <Box sx={{ position: 'relative', width: '100%' }}>
           <LanguageAutocomplete
+            id="audio-select"
             value={{
               id: currentLanguage?.id ?? '',
               localName: currentLanguage?.localName,
               nativeName: currentLanguage?.nativeName,
               slug: currentLanguage?.slug
             }}
             onChange={handleChange}
             languages={languages}
             loading={loading}
             disabled={loading}
             renderInput={renderInput(helperText)}
             renderOption={renderOption}
           />
-        </div>
-      </div>
-    </div>
+        </Box>
+      </Stack>
+    </Box>

Note: import Box, Stack, Typography, FormLabel from @mui/material.

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (3)

1-1: Remove unused import.

MockedResponse is unused.

-import { MockedProvider, MockedResponse } from '@apollo/client/testing'
+import { MockedProvider } from '@apollo/client/testing'

610-968: Optional: replace fireEvent with userEvent for realism.

Not required, but userEvent better models user intent for typing/clicking and avoids brittle role/DOM coupling.


1-1427: Remove unused MockedResponse import
In AudioTrackSelect.spec.tsx, update the Apollo import to only bring in MockedProviderMockedResponse is never referenced.

apps/watch/src/libs/watchContext/WatchContext.tsx (1)

384-395: Effect dependencies: guard against referential changes in initialState.

If initialState is re-created by the parent, the effect will re-run. If that’s intentional, OK; otherwise, destructure the arrays before useEffect to stabilize deps.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 15b08a0 and 1e28682.

📒 Files selected for processing (12)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (9 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (6 hunks)
  • apps/watch/src/components/NewVideoContentPage/VideoContentHero/HeroVideo/HeroVideo.tsx (3 hunks)
  • apps/watch/src/components/NewVideoContentPage/VideoContentHero/VideoContentHero.spec.tsx (6 hunks)
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx (1 hunks)
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelectContent/AudioLanguageSelectContent.spec.tsx (1 hunks)
  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx (1 hunks)
  • apps/watch/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoPlayer.tsx (3 hunks)
  • apps/watch/src/libs/watchContext/WatchContext.tsx (8 hunks)
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx (1 hunks)
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.spec.tsx (1 hunks)
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.tsx (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelectContent/AudioLanguageSelectContent.spec.tsx
🚧 Files skipped from review as they are similar to previous changes (5)
  • apps/watch/src/components/NewVideoContentPage/VideoContentHero/VideoContentHero.spec.tsx
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.tsx
  • apps/watch/src/components/VideoContentPage/VideoHero/VideoPlayer/VideoPlayer.tsx
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.spec.tsx
  • apps/watch/src/components/NewVideoContentPage/VideoContentHero/HeroVideo/HeroVideo.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx
  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx
  • apps/watch/src/libs/watchContext/WatchContext.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
🧠 Learnings (3)
📚 Learning: 2025-07-27T21:59:10.507Z
Learnt from: CR
PR: JesusFilm/core#0
File: .cursor/rules/watch-modern.mdc:0-0
Timestamp: 2025-07-27T21:59:10.507Z
Learning: Applies to apps/watch-modern/**/*.test.{tsx,jsx} : Use 'testing-library/react' npm package for frontend tests.

Applied to files:

  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
📚 Learning: 2025-07-27T21:59:04.362Z
Learnt from: CR
PR: JesusFilm/core#0
File: .cursor/rules/apps.mdc:0-0
Timestamp: 2025-07-27T21:59:04.362Z
Learning: Applies to apps/**/*.test.{js,jsx,ts,tsx} : Use 'testing-library/react' npm package for frontend tests.

Applied to files:

  • apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx
  • apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx
📚 Learning: 2025-07-15T03:19:49.322Z
Learnt from: Kneesal
PR: JesusFilm/core#7171
File: apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.spec.tsx:447-451
Timestamp: 2025-07-15T03:19:49.322Z
Learning: In the SubtitlesSelect component tests, the team intentionally uses integration tests that verify interactions work without errors rather than explicitly testing function calls, with explanatory comments to inform future developers of this testing strategy.

Applied to files:

  • apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
🧬 Code graph analysis (1)
apps/watch/src/libs/watchContext/WatchContext.tsx (2)
apps/watch/src/libs/localeMapping/index.ts (1)
  • LANGUAGE_MAPPINGS (3-3)
apps/watch/src/libs/localeMapping/localeMapping.ts (1)
  • LANGUAGE_MAPPINGS (22-204)
🪛 GitHub Actions: autofix.ci
apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx

[warning] 32-32: ESLint: 'mockGetCookie' is assigned a value but never used. (@typescript-eslint/no-unused-vars)

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx

[warning] 36-36: ESLint: 'dispatch' is assigned a value but never used. (@typescript-eslint/no-unused-vars)

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx

[warning] 1-1: ESLint: 'MockedResponse' is defined but never used. (@typescript-eslint/no-unused-vars)


[warning] 1-1: ESLint: 'MockedResponse' is defined but never used. (@typescript-eslint/no-unused-vars)


[error] 1031-1031: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1037-1037: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1125-1125: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1131-1131: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1204-1204: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1210-1210: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1287-1287: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1293-1293: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1346-1346: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1352-1352: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1405-1405: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)


[error] 1411-1411: ESLint: Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the void operator. (@typescript-eslint/no-floating-promises)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Deploy Preview (watch, 7548/merge, pull_request, 22)
  • GitHub Check: build (22)
🔇 Additional comments (6)
apps/watch/src/components/VideoContainerPage/AudioLanguageSelect/AudioLanguageSelect.spec.tsx (2)

248-254: LGTM: removed unused getByText.

Good cleanup—keeps the test focused on test IDs and avoids unused variable warnings.


4-6: Verified query and type imports remain correct after refactor. GET_LANGUAGES_SLUG and GetLanguagesSlug still exist and AudioLanguageSelect continues to use them, not the variant query.

apps/watch/src/components/VideoContentPage/VideoContentPage.spec.tsx (2)

1-1: Import cleanup LGTM

Removing unused named imports keeps the test lean. No behavior change.


20-22: Keep the module mock; no change needed

The module-level jest.mock for cookieHandler is still appropriate even after removing the unused alias.

apps/watch/src/libs/watchContext/useLanguageActions/useLanguageActions.spec.tsx (1)

1-1: LGTM: correct test utilities and public API usage.

Using renderHook from testing-library/react and consuming WatchProvider/WatchInitialState directly is aligned with our testing guidance.

Also applies to: 6-6

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.tsx (1)

107-113: Verify htmlFor/id association for accessibility.

If LanguageAutocomplete doesn’t forward the id prop to the input, the label’s htmlFor="audio-select" won’t be associated. Ensure the underlying TextField gets id="audio-select" (either via LanguageAutocomplete’s id prop or within renderInput).

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 (5)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (5)

513-547: Update to new language shapes; this test still uses legacy LanguageWithSlug.

The spec should no longer rely on videoAudioLanguages or nested LanguageWithSlug. Use videoAudioLanguagesIdsAndSlugs and a flat { id, slug } currentAudioLanguage.

-      videoAudioLanguages: [
-        {
-          __typename: 'LanguageWithSlug' as const,
-          slug: 'spanish', // Only Spanish available for this video
-          language: {
-            __typename: 'Language' as const,
-            id: '496',
-            slug: 'spanish',
-            name: [
-              {
-                __typename: 'LanguageName' as const,
-                value: 'Spanish',
-                primary: true
-              }
-            ]
-          }
-        }
-      ],
-      currentAudioLanguage: {
-        __typename: 'LanguageWithSlug' as const,
-        slug: 'spanish', // Video is playing in Spanish (not user's preference)
-        language: {
-          __typename: 'Language' as const,
-          id: '496',
-          slug: 'spanish',
-          name: [
-            {
-              __typename: 'LanguageName' as const,
-              value: 'Spanish',
-              primary: true
-            }
-          ]
-        }
-      }
+      videoAudioLanguagesIdsAndSlugs: [{ id: '496', slug: 'spanish' }],
+      currentAudioLanguage: { id: '496', slug: 'spanish' }

611-714: Turn this into a real interaction test; remove manual setIndexUiState invocation.

Right now the test only exercises the mock, not the component. Select an option via the UI and assert the callback.

-      // Test the setIndexUiState callback behavior that would be called when handleChange is triggered
-      const mockPrevState = {
-        refinementList: {
-          existingFilter: ['value']
-        }
-      }
-
-      // Simulate what would happen when handleChange is called with a language that has localName
-      const languageOption = {
-        id: '496',
-        localName: 'Spanish',
-        nativeName: 'Español',
-        slug: 'spanish'
-      }
-
-      // This simulates the logic in handleChange function
-      if (mockInstantSearch && languageOption.localName) {
-        mockInstantSearch.setIndexUiState((prev: any) => ({
-          ...prev,
-          refinementList: {
-            ...prev.refinementList,
-            languageEnglishName: [languageOption.localName ?? '']
-          }
-        }))
-      }
-
-      // Verify that setIndexUiState was called
-      expect(mockSetIndexUiState).toHaveBeenCalled()
-
-      // Verify the function passed to setIndexUiState updates the refinementList correctly
-      const setIndexUiStateCallback = mockSetIndexUiState.mock.calls[0][0]
-      const result = setIndexUiStateCallback(mockPrevState)
-      expect(result).toEqual({
-        refinementList: {
-          existingFilter: ['value'],
-          languageEnglishName: ['Spanish']
-        }
-      })
-
-      // Verify that the language actions are still called correctly
-      // Note: In a real scenario, this would be called when the user selects a language
-      // Here we're testing that the instant search integration doesn't interfere with core functionality
-      expect(mockUpdateAudioLanguage).not.toHaveBeenCalled() // Since we didn't actually trigger onChange
+      await userEvent.click(screen.getByRole('combobox'))
+      await waitFor(() => {
+        expect(
+          screen.getByRole('option', { name: 'Spanish' })
+        ).toBeInTheDocument()
+      })
+      await userEvent.click(screen.getByRole('option', { name: 'Spanish' }))
+
+      expect(mockSetIndexUiState).toHaveBeenCalled()
+      const setIndexUiStateCallback = mockSetIndexUiState.mock.calls[0][0]
+      const mockPrevState = { refinementList: { existingFilter: ['value'] } }
+      const result = setIndexUiStateCallback(mockPrevState)
+      expect(result).toEqual({
+        refinementList: {
+          existingFilter: ['value'],
+          languageEnglishName: ['Spanish']
+        }
+      })

767-771: This likely never triggers onChange; select an option instead of changing the input value.

Using fireEvent.change on the combobox input won’t commit a selection, so the “not called” assertion may be a false negative.

-      fireEvent.click(screen.getByRole('combobox'))
-      fireEvent.change(screen.getByRole('combobox'), {
-        target: { value: '496' }
-      })
+      await userEvent.click(screen.getByRole('combobox'))
+      await waitFor(() => {
+        expect(
+          screen.getByRole('option', { name: 'Spanish' })
+        ).toBeInTheDocument()
+      })
+      await userEvent.click(screen.getByRole('option', { name: 'Spanish' }))

1389-1390: Replace legacy videoAudioLanguages with videoAudioLanguagesIdsAndSlugs.

Keeps the spec consistent with new state shape.

-        videoAudioLanguages: []
+        videoAudioLanguagesIdsAndSlugs: []

828-835: Ensure actual option selection and undefined localName in the failing test
Replace the fireEvent calls in the “should not update instant search when language has no localName” test with userEvent clicks on the combobox and on the “Spanish” option:

- fireEvent.click(screen.getByRole('combobox'))
- fireEvent.change(screen.getByRole('combobox'), {
-   target: { value: '496' }
- })
+ await userEvent.click(screen.getByRole('combobox'))
+ await waitFor(() => {
+   expect(
+     screen.getByRole('option', { name: 'Spanish' })
+   ).toBeInTheDocument()
+ })
+ await userEvent.click(screen.getByRole('option', { name: 'Spanish' }))

Additionally, update your test fixture for id ‘496’ so it only includes nativeName (e.g.
{ id: '496', nativeName: 'Español', slug: 'spanish' }), omitting localName to guarantee language.localName === undefined.

♻️ Duplicate comments (2)
apps/watch/pages/watch/[part1]/[part2].tsx (1)

156-163: Fix id mismatch: passing Relay id to a databaseId query.
The query uses idType: databaseId, but variables.id is contentData.content.id (Relay node id). Pass databaseId instead (cast to string if needed), or change the query to accept SLUG. This was noted earlier; still unresolved here.

Apply:

       >({
-        query: GET_VARIANT_LANGUAGES_ID_AND_SLUG,
-        variables: {
-          id: contentData.content.id
-        }
+        query: GET_VARIANT_LANGUAGES_ID_AND_SLUG,
+        variables: {
+          id: String(contentData.content.databaseId)
+        }
       })

Run to confirm VideoContentFields includes databaseId; if missing, extend the fragment:

#!/bin/bash
# Find VideoContentFields fragment and show its fields
rg -nP -C2 'fragment\s+VideoContentFields\s+on\s+Video' --type=ts --type=tsx --type=graphql
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (1)

171-177: Fix id mismatch: using Relay id with idType: databaseId.
Same issue here; pass databaseId (cast) or change query idType.

Apply:

       >({
-        query: GET_VARIANT_LANGUAGES_ID_AND_SLUG,
-        variables: {
-          id: contentData.content.id
-        }
+        query: GET_VARIANT_LANGUAGES_ID_AND_SLUG,
+        variables: {
+          id: String(contentData.content.databaseId)
+        }
       })
🧹 Nitpick comments (5)
apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (5)

1-1: Remove unused GraphQL testing dependency to speed up tests.

MockedProvider is unnecessary here (component no longer issues GraphQL queries). Drop the import.

-import { MockedProvider } from '@apollo/client/testing'

83-88: Inline WatchProvider; drop MockedProvider wrapper (repeat across file).

Eliminates needless providers and cuts render overhead.

-      <MockedProvider mocks={[]} addTypename={false}>
-        <WatchProvider initialState={defaultInitialState}>
-          <AudioTrackSelect />
-        </WatchProvider>
-      </MockedProvider>
+      <WatchProvider initialState={defaultInitialState}>
+        <AudioTrackSelect />
+      </WatchProvider>

Apply the same change to all other render() calls in this spec.


59-59: Use userEvent.setup() for reliable async input and timers.

Avoids global state and aligns with Testing Library guidance.

 describe('AudioTrackSelect', () => {
+  let user: ReturnType<typeof userEvent.setup>
@@
   beforeEach(() => {
+    user = userEvent.setup()
     useRouterMock.mockReturnValue(mockRouter)
     useTranslationMock.mockReturnValue({ t: mockT })
     useInstantSearchMock.mockReturnValue(undefined) // Default to no instant search
     jest.clearAllMocks()
   })

Follow-up: replace await userEvent.click(...) with await user.click(...) throughout.

Also applies to: 74-79


93-93: Avoid querySelector('svg'); assert via accessible query or test id.

Tag selectors are brittle. Prefer a stable handle.

-    expect(document.querySelector('svg')).toBeInTheDocument() // SpatialAudioOffOutlinedIcon
+    expect(screen.getByTestId('audio-track-icon')).toBeInTheDocument()

Support in component (outside this file):

// AudioTrackSelect.tsx
<SpatialAudioOffOutlinedIcon data-testid="audio-track-icon" />

939-941: Prefer userEvent.type over fireEvent.change for typing.

Simulates real user input and opens the listbox reliably.

-      await fireEvent.change(input, { target: { value: 'Spanish' } })
+      await userEvent.type(input, 'Spanish')
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 1e28682 and 9f38696.

📒 Files selected for processing (5)
  • apps/watch/pages/watch/[part1]/[part2].tsx (5 hunks)
  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (6 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (15 hunks)
  • apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.tsx (3 hunks)
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.spec.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • apps/watch/src/libs/watchContext/useSubtitleUpdate/useSubtitleUpdate.spec.tsx
  • apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.tsx
🧰 Additional context used
📓 Path-based instructions (3)
**/*.{ts,tsx,js,jsx}

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

**/*.{ts,tsx,js,jsx}: Use early returns whenever possible to make the code more readable.
Use descriptive variable and function/const names.
Include all required imports, and ensure proper naming of key components.

Files:

  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
**/*.{ts,tsx}

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

Define a type if possible.

Files:

  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
apps/**/*.{js,jsx,ts,tsx}

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

apps/**/*.{js,jsx,ts,tsx}: Always use MUI over HTML elements; avoid using CSS or tags.
Use descriptive variable and function/const names. Also, event functions should be named with a “handle” prefix, like “handleClick” for onClick and “handleKeyDown” for onKeyDown.
Implement accessibility features on elements. For example, a tag should have a tabindex=“0”, aria-label, on:click, and on:keydown, and similar attributes.

Files:

  • apps/watch/pages/watch/[part1]/[part2]/[part3].tsx
  • apps/watch/pages/watch/[part1]/[part2].tsx
  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
🧠 Learnings (3)
📚 Learning: 2025-07-27T21:59:10.507Z
Learnt from: CR
PR: JesusFilm/core#0
File: .cursor/rules/watch-modern.mdc:0-0
Timestamp: 2025-07-27T21:59:10.507Z
Learning: Applies to apps/watch-modern/**/*.test.{tsx,jsx} : Use 'testing-library/react' npm package for frontend tests.

Applied to files:

  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
📚 Learning: 2025-07-27T21:59:04.362Z
Learnt from: CR
PR: JesusFilm/core#0
File: .cursor/rules/apps.mdc:0-0
Timestamp: 2025-07-27T21:59:04.362Z
Learning: Applies to apps/**/*.test.{js,jsx,ts,tsx} : Use 'testing-library/react' npm package for frontend tests.

Applied to files:

  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
📚 Learning: 2025-07-15T03:19:49.322Z
Learnt from: Kneesal
PR: JesusFilm/core#7171
File: apps/watch/src/components/LanguageSwitchDialog/SubtitlesSelect/SubtitlesSelect.spec.tsx:447-451
Timestamp: 2025-07-15T03:19:49.322Z
Learning: In the SubtitlesSelect component tests, the team intentionally uses integration tests that verify interactions work without errors rather than explicitly testing function calls, with explanatory comments to inform future developers of this testing strategy.

Applied to files:

  • apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx
🧬 Code graph analysis (2)
apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (3)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (15-18)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (27-29)
  • GetVariantLanguagesIdAndSlugVariables (31-33)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (8-20)
apps/watch/pages/watch/[part1]/[part2].tsx (3)
apps/watch/src/libs/watchContext/WatchContext.tsx (1)
  • AudioLanguageData (15-18)
apps/watch/__generated__/GetVariantLanguagesIdAndSlug.ts (2)
  • GetVariantLanguagesIdAndSlug (27-29)
  • GetVariantLanguagesIdAndSlugVariables (31-33)
apps/watch/src/libs/useVariantLanguagesIdAndSlugQuery/useVariantLanguagesIdAndSlugQuery.ts (1)
  • GET_VARIANT_LANGUAGES_ID_AND_SLUG (8-20)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: Deploy Preview (watch, 7548/merge, pull_request, 22)
  • GitHub Check: test (22, 3/3)
  • GitHub Check: build (22)
  • GitHub Check: lint (22)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (17)
apps/watch/pages/watch/[part1]/[part2].tsx (7)

10-12: LGTM: correct generated type imports added.


26-26: LGTM: using central GET_VARIANT_LANGUAGES_ID_AND_SLUG.


46-48: Props renamed to new shapes match WatchContext AudioLanguageData/string[] model.


66-70: Component props updated consistently.


80-82: Initial watch state wired to new props.


152-154: Good: pre-initialize arrays to [].


181-182: LGTM: props return matches new names.

apps/watch/pages/watch/[part1]/[part2]/[part3].tsx (9)

12-14: LGTM: correct generated type imports added.


33-33: LGTM: central query import.


37-40: LGTM: AudioLanguageData import aligns with WatchContext.


63-65: Props updated to new shapes.


70-72: Component destructuring consistent.


84-86: Initial watch state wired to new fields.


167-168: Good: arrays initialized.


179-188: LGTM: mappings with nullish fallback prevent undefined.


196-197: LGTM: returned props align with new names.

apps/watch/src/components/LanguageSwitchDialog/AudioTrackSelect/AudioTrackSelect.spec.tsx (1)

265-269: LGTM: Consistent migration to videoAudioLanguagesIdsAndSlugs.

Tests correctly use the new { id, slug } shape in most places and validate reload semantics.

Also applies to: 450-459, 1006-1014, 1183-1188, 1262-1270, 1330-1331

@tataihono tataihono enabled auto-merge August 29, 2025 02:57
@tataihono tataihono disabled auto-merge August 29, 2025 02:57
@tataihono tataihono merged commit 2266855 into main Aug 29, 2025
25 checks passed
@tataihono tataihono deleted the 25-00-NC-fix-condense-watch-queries branch August 29, 2025 02:57
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