Skip to content

feat(angular-talks): add list of latest angular talks to home page#545

Open
dawidg-hoa wants to merge 5 commits into
mainfrom
feat/angular-talks
Open

feat(angular-talks): add list of latest angular talks to home page#545
dawidg-hoa wants to merge 5 commits into
mainfrom
feat/angular-talks

Conversation

@dawidg-hoa

@dawidg-hoa dawidg-hoa commented Jun 12, 2026

Copy link
Copy Markdown

Add new section to home page with latest angular talks.
The list of three latest videos is fetched from word press, which are then displayed as preview cards at the bottom of the home page. If user clicks the preview card a dialog is displayed with embedded youtube player.

Changes:

  • add new api endpoint for fetching videos from word press. It returns only basic information like video ID, video title and the event name during which the talk happened,
  • add data-access layer for fetching videos in the frontend,
  • add new feature lib with angular-talks-container,
  • add UI libs for video card, dialog, and video player components,
  • refactor the article-list-title component into a more general section-title component as the same UI is needed also for videos list section. Pretty much only a rename and location change was needed as it wasn't coupled with articles.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added videos/talks section to the blog homepage displaying Angular talks
    • Integrated YouTube video player with modal dialog for full-screen viewing
    • Responsive video grid with pagination support
  • Internationalization

    • Added English and Polish translations for talks and video player labels
  • UI/UX Improvements

    • Added skeleton loaders for better loading states
    • Refactored section title component for broader reusability

@coderabbitai

coderabbitai Bot commented Jun 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds video preview contracts, server endpoints, Angular video data and UI libraries, and homepage rendering for a latest videos section. It also replaces the older article-list title component with a shared section-title component and adds matching translations, aliases, and locator updates.

Changes

Videos homepage feature

Layer / File(s) Summary
Shared contracts and workspace wiring
libs/blog-contracts/videos/*, libs/blog-bff/videos/api/*, tsconfig.base.json
Adds the VideoPreview contract library, scaffolds the BFF videos API library, and wires new path aliases for video-related packages.
Server video endpoints
libs/blog-bff/videos/api/src/lib/*, apps/blog-bff/src/main.ts, apps/blog-analog/src/server/routes/api/videos/index.get.ts
Adds server handlers that paginate WordPress video results, map them into VideoPreview, and expose /videos routes.
Client video data and playback UI
libs/blog/videos/data-access/*, libs/blog/videos/feature-list/*, libs/blog/videos/ui-video-card/*, libs/blog/videos/ui-video-dialog/*, libs/blog/videos/ui-youtube-video-player/*
Adds the videos store and service, latest videos list container, video card and skeleton UI, dialog manager and dialog component, and the YouTube player component.
Homepage integration and section-title migration
libs/blog/home/feature-home/*, libs/blog/shared/ui-section-title/*, libs/blog/articles/feature-list/.../category-section-container.*, apps/blog*/src/assets/i18n/*.json, apps/blog-e2e/src/lib/elements/category-container.element.ts
Renders the latest videos section on the homepage, replaces article-list titles with section titles, updates test ids and skeleton property naming, and adds talks and videoPlayer translations.

Sequence Diagram(s)

sequenceDiagram
  participant HomePage
  participant LatestVideosList
  participant VideosAPI
  participant WordPress
  participant VideoDialog

  HomePage->>LatestVideosList: render latest videos section
  LatestVideosList->>VideosAPI: GET /videos?take=3
  VideosAPI->>WordPress: fetch yt-video entries
  WordPress-->>VideosAPI: video items + x-wp-total
  VideosAPI-->>LatestVideosList: ArrayResponse<VideoPreview>
  LatestVideosList->>VideoDialog: open selected video
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

Suggested reviewers

  • DDonochVA
  • mateuszbasinski
  • majahendzel-va

Poem

🐇 I found some talks beside the page,
with cards and dialogs on a stage.
A title changed, a player gleamed,
new routes and previews softly streamed.
I thump with joy: the burrow sings!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the primary change: adding a feature to display Angular talks on the home page, which aligns with the substantial new videos functionality across frontend and backend.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/angular-talks

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

❤️ Share

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

@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://d58a036b-blog-bff-dev.contact-ef8.workers.dev

@dawidg-hoa dawidg-hoa force-pushed the feat/angular-talks branch from 35d3234 to 3142f09 Compare June 16, 2026 11:38
@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://004edb31-blog-bff-dev.contact-ef8.workers.dev

@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://3c0f25ea-blog-bff-dev.contact-ef8.workers.dev

@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://d30cf12f-blog-bff-dev.contact-ef8.workers.dev

@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://6ecf1470-blog-bff-dev.contact-ef8.workers.dev

@dawidg-hoa dawidg-hoa marked this pull request as ready for review June 23, 2026 14:10
@valueadd-robot

Copy link
Copy Markdown

PR is detected, will deploy to dev environment

@valueadd-robot

Copy link
Copy Markdown

Deployed to dev environment
Branch: feat/angular-talks
Deploy URL: https://0e81ce24-blog-bff-dev.contact-ef8.workers.dev

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (2)
libs/blog/videos/ui-video-card/src/lib/types/video-card.ts (1)

1-5: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Avoid duplicating the shared video contract shape.

VideoCard currently mirrors VideoPreview exactly, which risks future drift. Reuse the shared contract type (alias/extends) to keep server/client shape aligned.

Proposed refactor
+import { VideoPreview } from '`@angular-love/blog/contracts/videos`';
+
-export interface VideoCard {
-  videoId: string;
-  title: string;
-  eventName: string;
-}
+export type VideoCard = VideoPreview;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/blog/videos/ui-video-card/src/lib/types/video-card.ts` around lines 1 -
5, `VideoCard` is duplicating the exact shape of the shared video contract,
which can drift over time. Update the `VideoCard` type in the `video-card` types
module to reuse the existing shared contract type used by `VideoPreview` instead
of redefining the same fields, either by aliasing or extending that shared type
so both stay aligned.
libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.stories.ts (1)

41-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove unused video args from the Skeleton story.

VideoCardSkeletonComponent doesn’t consume video, so this arg only adds noise in Storybook controls and can confuse the story contract.

Proposed cleanup
 export const Skeleton: StoryObj<VideoCardSkeletonComponent> = {
-  args: {
-    video: video,
-  },
   render: (args) => ({
     props: args,
     template: `
       <div style="width: 405px;">
         <al-video-card-skeleton />
       </div>
     `,
   }),
 };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.stories.ts`
around lines 41 - 44, The Skeleton story for VideoCardSkeletonComponent is
passing an unused video arg, which only adds noise to Storybook controls. Update
the Skeleton story in video-card.stories.ts to remove the video entry from the
args object and keep the story aligned with the actual component contract.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@libs/blog-bff/videos/api/src/lib/api.ts`:
- Line 25: The `total` field assignment using
`Number(response.headers.get('x-wp-total'))` lacks validation and can silently
produce NaN or incorrect values when the header is malformed, missing, or
invalid. Replace this with guarded parsing that explicitly checks if the header
exists and contains a valid numeric value, using a deterministic fallback value
(such as 0) when parsing fails or the header is absent. This ensures the total
field always has a valid, predictable value even with malformed response
headers.

In
`@libs/blog/videos/ui-video-dialog/src/lib/components/video-dialog/video-dialog.component.scss`:
- Around line 2-5: The stylesheet animation name is not following the enforced
kebab-case Stylelint rule, so update the animation reference and the matching
`@keyframes` identifier in video-dialog.component.scss to use a kebab-case name.
Keep the animation declaration and the keyframes block in sync by renaming both
symbols together so the existing animation behavior in the video dialog remains
unchanged.

In
`@libs/blog/videos/ui-youtube-video-player/src/lib/components/youtube-video-player/youtube-video-player.component.ts`:
- Around line 31-36: The `lang` variable is being interpolated directly into the
YouTube embed URL query parameters without encoding, which can lead to query
parameter injection vulnerabilities and malformed embeds. In the `videoSrc`
computed property, encode the `lang` variable using `encodeURIComponent` before
interpolating it into the hl and cc_lang_pref query parameters, applying the
same encoding pattern already used for `videoId`.

---

Nitpick comments:
In
`@libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.stories.ts`:
- Around line 41-44: The Skeleton story for VideoCardSkeletonComponent is
passing an unused video arg, which only adds noise to Storybook controls. Update
the Skeleton story in video-card.stories.ts to remove the video entry from the
args object and keep the story aligned with the actual component contract.

In `@libs/blog/videos/ui-video-card/src/lib/types/video-card.ts`:
- Around line 1-5: `VideoCard` is duplicating the exact shape of the shared
video contract, which can drift over time. Update the `VideoCard` type in the
`video-card` types module to reuse the existing shared contract type used by
`VideoPreview` instead of redefining the same fields, either by aliasing or
extending that shared type so both stay aligned.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 445b3f90-084b-4a66-a0a4-bc96ff2dcb27

📥 Commits

Reviewing files that changed from the base of the PR and between dd649e4 and e523ad9.

⛔ Files ignored due to path filters (2)
  • apps/blog-analog/public/assets/icons/play.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/play.svg is excluded by !**/*.svg
📒 Files selected for processing (109)
  • apps/blog-analog/src/app/i18n/assets/en.json
  • apps/blog-analog/src/app/i18n/assets/pl.json
  • apps/blog-analog/src/server/routes/api/videos/index.get.ts
  • apps/blog-bff/src/main.ts
  • apps/blog-bff/wrangler.toml
  • apps/blog-e2e/src/lib/elements/category-container.element.ts
  • apps/blog/src/assets/i18n/en.json
  • apps/blog/src/assets/i18n/pl.json
  • libs/blog-bff/videos/api/.eslintrc.json
  • libs/blog-bff/videos/api/README.md
  • libs/blog-bff/videos/api/jest.config.ts
  • libs/blog-bff/videos/api/project.json
  • libs/blog-bff/videos/api/src/index.ts
  • libs/blog-bff/videos/api/src/lib/api.ts
  • libs/blog-bff/videos/api/src/lib/dtos.ts
  • libs/blog-bff/videos/api/src/lib/mappers.ts
  • libs/blog-bff/videos/api/src/lib/wp-videos.ts
  • libs/blog-bff/videos/api/tsconfig.json
  • libs/blog-bff/videos/api/tsconfig.lib.json
  • libs/blog-bff/videos/api/tsconfig.spec.json
  • libs/blog-contracts/videos/.eslintrc.json
  • libs/blog-contracts/videos/README.md
  • libs/blog-contracts/videos/jest.config.ts
  • libs/blog-contracts/videos/project.json
  • libs/blog-contracts/videos/src/index.ts
  • libs/blog-contracts/videos/src/lib/video-preview.ts
  • libs/blog-contracts/videos/tsconfig.json
  • libs/blog-contracts/videos/tsconfig.lib.json
  • libs/blog-contracts/videos/tsconfig.spec.json
  • libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.html
  • libs/blog/articles/feature-list/src/lib/category-section-container/category-section-container.component.ts
  • libs/blog/articles/ui-article-list-title/README.md
  • libs/blog/articles/ui-article-list-title/src/index.ts
  • libs/blog/home/feature-home/src/lib/home-page/home-page.component.html
  • libs/blog/home/feature-home/src/lib/home-page/home-page.component.ts
  • libs/blog/shared/ui-section-title/.eslintrc.json
  • libs/blog/shared/ui-section-title/README.md
  • libs/blog/shared/ui-section-title/jest.config.ts
  • libs/blog/shared/ui-section-title/project.json
  • libs/blog/shared/ui-section-title/src/index.ts
  • libs/blog/shared/ui-section-title/src/lib/ui-section-title/ui-section-title.component.html
  • libs/blog/shared/ui-section-title/src/lib/ui-section-title/ui-section-title.component.ts
  • libs/blog/shared/ui-section-title/src/test-setup.ts
  • libs/blog/shared/ui-section-title/tsconfig.json
  • libs/blog/shared/ui-section-title/tsconfig.lib.json
  • libs/blog/shared/ui-section-title/tsconfig.spec.json
  • libs/blog/videos/data-access/.eslintrc.json
  • libs/blog/videos/data-access/README.md
  • libs/blog/videos/data-access/jest.config.ts
  • libs/blog/videos/data-access/project.json
  • libs/blog/videos/data-access/src/index.ts
  • libs/blog/videos/data-access/src/lib/dto/videos.query.ts
  • libs/blog/videos/data-access/src/lib/infrastructure/videos.service.ts
  • libs/blog/videos/data-access/src/lib/state/videos-list.store.ts
  • libs/blog/videos/data-access/src/test-setup.ts
  • libs/blog/videos/data-access/tsconfig.json
  • libs/blog/videos/data-access/tsconfig.lib.json
  • libs/blog/videos/data-access/tsconfig.spec.json
  • libs/blog/videos/feature-list/.eslintrc.json
  • libs/blog/videos/feature-list/README.md
  • libs/blog/videos/feature-list/jest.config.ts
  • libs/blog/videos/feature-list/project.json
  • libs/blog/videos/feature-list/src/index.ts
  • libs/blog/videos/feature-list/src/lib/components/latest-videos-list-container/latest-videos-list-container.component.html
  • libs/blog/videos/feature-list/src/lib/components/latest-videos-list-container/latest-videos-list-container.component.ts
  • libs/blog/videos/feature-list/src/lib/services/video-dialog-manager.service.ts
  • libs/blog/videos/feature-list/src/test-setup.ts
  • libs/blog/videos/feature-list/tsconfig.json
  • libs/blog/videos/feature-list/tsconfig.lib.json
  • libs/blog/videos/feature-list/tsconfig.spec.json
  • libs/blog/videos/ui-video-card/.eslintrc.json
  • libs/blog/videos/ui-video-card/README.md
  • libs/blog/videos/ui-video-card/jest.config.ts
  • libs/blog/videos/ui-video-card/project.json
  • libs/blog/videos/ui-video-card/src/index.ts
  • libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card-skeleton.component.ts
  • libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.component.html
  • libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.component.ts
  • libs/blog/videos/ui-video-card/src/lib/components/video-card/video-card.stories.ts
  • libs/blog/videos/ui-video-card/src/lib/types/video-card.ts
  • libs/blog/videos/ui-video-card/src/test-setup.ts
  • libs/blog/videos/ui-video-card/tsconfig.json
  • libs/blog/videos/ui-video-card/tsconfig.lib.json
  • libs/blog/videos/ui-video-card/tsconfig.spec.json
  • libs/blog/videos/ui-video-dialog/.eslintrc.json
  • libs/blog/videos/ui-video-dialog/README.md
  • libs/blog/videos/ui-video-dialog/jest.config.ts
  • libs/blog/videos/ui-video-dialog/project.json
  • libs/blog/videos/ui-video-dialog/src/index.ts
  • libs/blog/videos/ui-video-dialog/src/lib/components/video-dialog/video-dialog.component.html
  • libs/blog/videos/ui-video-dialog/src/lib/components/video-dialog/video-dialog.component.scss
  • libs/blog/videos/ui-video-dialog/src/lib/components/video-dialog/video-dialog.component.ts
  • libs/blog/videos/ui-video-dialog/src/lib/types/video-dialog-data.ts
  • libs/blog/videos/ui-video-dialog/src/test-setup.ts
  • libs/blog/videos/ui-video-dialog/tsconfig.json
  • libs/blog/videos/ui-video-dialog/tsconfig.lib.json
  • libs/blog/videos/ui-video-dialog/tsconfig.spec.json
  • libs/blog/videos/ui-youtube-video-player/.eslintrc.json
  • libs/blog/videos/ui-youtube-video-player/README.md
  • libs/blog/videos/ui-youtube-video-player/jest.config.ts
  • libs/blog/videos/ui-youtube-video-player/project.json
  • libs/blog/videos/ui-youtube-video-player/src/index.ts
  • libs/blog/videos/ui-youtube-video-player/src/lib/components/youtube-video-player/youtube-video-player.component.html
  • libs/blog/videos/ui-youtube-video-player/src/lib/components/youtube-video-player/youtube-video-player.component.ts
  • libs/blog/videos/ui-youtube-video-player/src/test-setup.ts
  • libs/blog/videos/ui-youtube-video-player/tsconfig.json
  • libs/blog/videos/ui-youtube-video-player/tsconfig.lib.json
  • libs/blog/videos/ui-youtube-video-player/tsconfig.spec.json
  • tsconfig.base.json
💤 Files with no reviewable changes (2)
  • libs/blog/articles/ui-article-list-title/README.md
  • libs/blog/articles/ui-article-list-title/src/index.ts


return c.json(<ArrayResponse<VideoPreview>>{
data: response.data.map(mapWPVideoToVideoPreview),
total: Number(response.headers.get('x-wp-total')),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Harden total parsing from x-wp-total.

Number(response.headers.get('x-wp-total')) can silently yield an invalid/incorrect total value for malformed headers. Prefer guarded parsing with a deterministic fallback.

Proposed fix
+  const totalHeader = response.headers.get('x-wp-total');
+  const parsedTotal = totalHeader ? Number.parseInt(totalHeader, 10) : NaN;
+
   return c.json(<ArrayResponse<VideoPreview>>{
     data: response.data.map(mapWPVideoToVideoPreview),
-    total: Number(response.headers.get('x-wp-total')),
+    total: Number.isFinite(parsedTotal) ? parsedTotal : response.data.length,
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
total: Number(response.headers.get('x-wp-total')),
const totalHeader = response.headers.get('x-wp-total');
const parsedTotal = totalHeader ? Number.parseInt(totalHeader, 10) : NaN;
return c.json(<ArrayResponse<VideoPreview>>{
data: response.data.map(mapWPVideoToVideoPreview),
total: Number.isFinite(parsedTotal) ? parsedTotal : response.data.length,
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@libs/blog-bff/videos/api/src/lib/api.ts` at line 25, The `total` field
assignment using `Number(response.headers.get('x-wp-total'))` lacks validation
and can silently produce NaN or incorrect values when the header is malformed,
missing, or invalid. Replace this with guarded parsing that explicitly checks if
the header exists and contains a valid numeric value, using a deterministic
fallback value (such as 0) when parsing fails or the header is absent. This
ensures the total field always has a valid, predictable value even with
malformed response headers.

Comment on lines +2 to +5
animation: fadeInBottom 0.4s ease-out forwards;
}

@keyframes fadeInBottom {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Rename the animation keyframes to kebab-case.

fadeInBottom will trip the configured Stylelint rule. Please rename both the animation reference and the @keyframes block so the stylesheet stays lint-clean.

Suggested fix
 .dialog {
-  animation: fadeInBottom 0.4s ease-out forwards;
+  animation: fade-in-bottom 0.4s ease-out forwards;
 }
 
-@keyframes fadeInBottom {
+@keyframes fade-in-bottom {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
animation: fadeInBottom 0.4s ease-out forwards;
}
@keyframes fadeInBottom {
.dialog {
animation: fade-in-bottom 0.4s ease-out forwards;
}
`@keyframes` fade-in-bottom {
🧰 Tools
🪛 Stylelint (17.13.0)

[error] 5-5: Expected keyframe name "fadeInBottom" to be kebab-case (keyframes-name-pattern)

(keyframes-name-pattern)

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

In
`@libs/blog/videos/ui-video-dialog/src/lib/components/video-dialog/video-dialog.component.scss`
around lines 2 - 5, The stylesheet animation name is not following the enforced
kebab-case Stylelint rule, so update the animation reference and the matching
`@keyframes` identifier in video-dialog.component.scss to use a kebab-case name.
Keep the animation declaration and the keyframes block in sync by renaming both
symbols together so the existing animation behavior in the video dialog remains
unchanged.

Source: Linters/SAST tools

Comment on lines +31 to +36
protected readonly videoSrc = computed(() => {
const lang = this.lang();
const videoId = encodeURIComponent(this.videoId());
return this.domSanitizer.bypassSecurityTrustResourceUrl(
`https://www.youtube-nocookie.com/embed/${videoId}?rel=0&autoplay=1&hl=${lang}&cc_lang_pref=${lang}`,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Encode locale before interpolating into trusted embed URL.

Line 35 interpolates lang directly into query params while using bypassSecurityTrustResourceUrl; encode it first to prevent query-parameter injection and malformed embeds.

Proposed fix
   protected readonly videoSrc = computed(() => {
-    const lang = this.lang();
+    const lang = encodeURIComponent(this.lang());
     const videoId = encodeURIComponent(this.videoId());
     return this.domSanitizer.bypassSecurityTrustResourceUrl(
       `https://www.youtube-nocookie.com/embed/${videoId}?rel=0&autoplay=1&hl=${lang}&cc_lang_pref=${lang}`,
     );
   });
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
protected readonly videoSrc = computed(() => {
const lang = this.lang();
const videoId = encodeURIComponent(this.videoId());
return this.domSanitizer.bypassSecurityTrustResourceUrl(
`https://www.youtube-nocookie.com/embed/${videoId}?rel=0&autoplay=1&hl=${lang}&cc_lang_pref=${lang}`,
);
protected readonly videoSrc = computed(() => {
const lang = encodeURIComponent(this.lang());
const videoId = encodeURIComponent(this.videoId());
return this.domSanitizer.bypassSecurityTrustResourceUrl(
`https://www.youtube-nocookie.com/embed/${videoId}?rel=0&autoplay=1&hl=${lang}&cc_lang_pref=${lang}`,
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@libs/blog/videos/ui-youtube-video-player/src/lib/components/youtube-video-player/youtube-video-player.component.ts`
around lines 31 - 36, The `lang` variable is being interpolated directly into
the YouTube embed URL query parameters without encoding, which can lead to query
parameter injection vulnerabilities and malformed embeds. In the `videoSrc`
computed property, encode the `lang` variable using `encodeURIComponent` before
interpolating it into the hl and cc_lang_pref query parameters, applying the
same encoding pattern already used for `videoId`.

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