Skip to content

Add genre breakdown chart to home page#1924

Draft
rudf0rd wants to merge 1 commit intomainfrom
feat/home-genre-breakdown
Draft

Add genre breakdown chart to home page#1924
rudf0rd wants to merge 1 commit intomainfrom
feat/home-genre-breakdown

Conversation

@rudf0rd
Copy link
Copy Markdown
Contributor

@rudf0rd rudf0rd commented Mar 21, 2026

Scene Setting: A Clear Description

Adds a 14-day genre breakdown stacked bar chart to the authenticated home page. The chart visualizes watching habits by genre over the last two weeks, showing the top 5 genres plus an "Other" bucket. Each day is a stacked bar with genre-colored segments, accompanied by a legend with percentage breakdowns.

Not 100% excited by the design, so opening as a draft. My pref would be to merge and then iterate design after.

What's included:

  • GenreBreakdown.svelte — frosted glass card with stacked bar chart and legend
  • useGenreBreakdown.ts — pure computeGenreBreakdown function that aggregates movie + show activity history into per-day genre segments with weighted scoring for multi-genre titles
  • activityHistoryParams.ts — shared paginated history fetcher (1-year lookback)
  • i18n keys for all 20 locales (3 keys: today label, "other" genre label, section header)
  • Only renders for authenticated users on tablet/desktop via RenderFor guard
  • Hidden when user has no activity in the window (no empty state)

Show, Don't Tell: Screenshots and Videos

CleanShot 2026-03-21 at 12 32 32@2x

Hidden on mobile.

Testing: The Dress Rehearsal

The core logic is covered by unit tests in useGenreBreakdown.spec.ts (8 test cases):

  • Empty data returns 14 days with zero totals
  • Entries group correctly by calendar day
  • Top 5 genre limit enforced, overflow bucketed into "other"
  • Entries with no genres treated as "other"
  • Show genres pulled from the show field (not episode)
  • Entries outside the 14-day window are excluded
  • Legend colors are valid hex values
  • Percentage distribution reflects weighted genre counts (e.g., 67/33 split)

Add a 14-day genre breakdown chart with stacked bars showing watching
distribution across genres. Includes frosted glass design and "Other"
bucket for long-tail genres.

Responsive: hidden on mobile via RenderFor device guard.
@rudf0rd rudf0rd requested review from seferturan and vladjerca March 21, 2026 19:34
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request enhances the user experience on the home page by introducing a new data visualization feature. Users can now gain insights into their recent content consumption patterns through a genre breakdown chart, which dynamically displays their watching habits over the past two weeks. This addition provides a personalized and engaging overview of their media preferences, making the home page more informative and interactive.

Highlights

  • New Genre Breakdown Chart: Introduced a new 14-day genre breakdown stacked bar chart on the authenticated home page, visualizing user watching habits by genre.
  • Genre Aggregation Logic: Implemented a pure function to aggregate movie and show activity history into per-day genre segments, including weighted scoring for multi-genre titles and bucketing into 'Other' for less common genres.
  • Internationalization Support: Added new i18n keys for 'Today', 'Other' genre label, and 'Genre Breakdown' header across 20 different locales.
  • Conditional Rendering: The new chart is rendered only for authenticated users on tablet and desktop devices and is hidden when there is no activity within the 14-day window.
  • Activity History Utility: Created a shared utility for fetching paginated movie and show activity history with a 1-year lookback period to support the genre breakdown calculation.
  • Unit Testing: Comprehensive unit tests were added for the core genre breakdown logic, covering various scenarios like empty data, grouping, genre limits, and percentage calculations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@rudf0rd rudf0rd changed the title feat(stats): add genre breakdown chart to home page Add genre breakdown chart to home page Mar 21, 2026
@deepsource-io
Copy link
Copy Markdown

deepsource-io Bot commented Mar 21, 2026

DeepSource Code Review

We reviewed changes in 66dbdef...5c8315a on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Mar 21, 2026 7:35p.m. Review ↗
Test coverage Mar 21, 2026 7:35p.m. Review ↗

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

The pull request introduces a new genre breakdown chart to the home page, along with the necessary internationalization keys and data processing logic. The implementation is well-tested with comprehensive unit tests. I've identified a few opportunities to improve code clarity and maintainability by replacing magic numbers with named constants and removing redundant nullish coalescing operators.

} from '$lib/requests/queries/users/showActivityHistoryQuery.ts';
import { usePaginatedListQuery } from '../../lists/stores/usePaginatedListQuery.ts';

const historyLimit = 1000;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The value 1000 is a magic number. It should be extracted into a named constant to improve readability and maintainability. This adheres to the repository's naming conventions for constants.

Suggested change
const historyLimit = 1000;
const HISTORY_LIMIT = 1000;

Comment on lines +30 to +36
const genreColors = [
'#a87cf0', // purple - slot 1
'#6ea1f7', // blue - slot 2
'#4ecdc4', // teal - slot 3
'#7bc67e', // green - slot 4
'#f0a04b', // orange - slot 5
'#555555', // gray - "Other"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The genreColors array contains hardcoded hex values. While the array itself is a constant, the individual color values could be defined as named constants (e.g., COLOR_PURPLE_SLOT_1) or retrieved from a centralized theme/design system to improve maintainability and ensure consistency across the application. This aligns with the spirit of the repository's naming conventions for constants.

'#555555', // gray - "Other"
];

const topGenreCount = 5;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The value 5 is a magic number representing the topGenreCount. It should be extracted into a named constant (e.g., TOP_GENRE_COUNT) to improve readability and maintainability, adhering to the repository's naming conventions for constants.

Suggested change
const topGenreCount = 5;
const TOP_GENRE_COUNT = 5;


const topGenreCount = 5;
export const DAY_COUNT = 14;
const genresPerEntry = 2;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The value 2 is a magic number representing genresPerEntry. It should be extracted into a named constant (e.g., MAX_GENRES_PER_ENTRY) to improve readability and maintainability, adhering to the repository's naming conventions for constants.

Suggested change
const genresPerEntry = 2;
const MAX_GENRES_PER_ENTRY = 2;

),
);

const rangeStart = days[0] ?? today;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The nullish coalescing operator ?? today is redundant here. The days array is initialized with DAY_COUNT elements, so days[0] will always be defined. This can be simplified to const rangeStart = days[0];

Suggested change
const rangeStart = days[0] ?? today;
const rangeStart = days[0];


// Build day data
const dayData: GenreDayData[] = days.map((date) => {
const dayCounts = dayGenreCounts.get(getDayKey(date)) ?? new Map<string, number>();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The nullish coalescing operator ?? new Map<string, number>() is redundant here. The dayGenreCounts map is initialized with an entry for every day in the days array, so dayGenreCounts.get(getDayKey(date)) will always return a Map. This can be simplified to const dayCounts = dayGenreCounts.get(getDayKey(date));

Suggested change
const dayCounts = dayGenreCounts.get(getDayKey(date)) ?? new Map<string, number>();
const dayCounts = dayGenreCounts.get(getDayKey(date));

: toTranslatedGenre(genre),
percentage:
totalWeight > 0 ? Math.round((count / totalWeight) * 100) : 0,
color: genreColors[Math.min(i, genreColors.length - 1)] ?? '#555555',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The fallback color '#555555' is a hardcoded hex value. It should be defined as a named constant (e.g., DEFAULT_GENRE_COLOR) to improve maintainability and ensure consistency across the application. This aligns with the repository's naming conventions for constants.

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.

1 participant