Conversation
- Simplify Stats page to card-based UI with period filtering (month/last_month/year/all) - Replace expandable sections with NavigationLink cards to detail pages - Add custom profile-style headers (circular back button, scroll-aware title) to all detail views - Fix intermittent data: genres now include series with episodes watched in period - Fix stale cache: invalidate stats on episode create/delete and review create/update/delete - Reduce cache TTL to 10min for period-scoped stats (month/last_month) - Add posterPath to genre response schema so Fastify doesn't strip it - Add statsCardBackground color with proper light/dark mode contrast - Shared statsPoster helper: 70% width, standard 16px corner radius, optional rating badge - Add singular localization strings (favoriteGenre, bestReview) across 7 languages Co-authored-by: Cursor <cursoragent@cursor.com>
- Introduced a new MonthSection model to manage monthly statistics, improving data organization and display. - Updated ProfileStatsView to support a timeline mode, allowing users to view stats by month with enhanced visual elements. - Added new localization strings for timeline and comparison metrics, improving user experience across multiple languages. - Refactored various components to streamline data handling and improve code readability, including the removal of unused methods and properties. These changes aim to provide users with a more engaging and informative stats experience, aligning with modern UI practices.
- Enhanced the layout of StatsShareCardView to utilize a new card size and updated gradient background for better visual appeal. - Adjusted text sizes and spacing for improved readability and consistency across different screen sizes. - Refactored the rendering logic in ProfileStatsView to accommodate the new card dimensions, ensuring high-quality image generation for sharing. - Improved accessibility by refining color contrasts and text legibility. These changes aim to create a more engaging and visually appealing user experience in the stats section.
- Fix floating scrubber: use absolute drag offset from start index instead of cumulative reset, ensuring reliable month navigation - Localize month names in scrubber and share card using app language - Pre-download poster images before rendering share card snapshot - Use PlotistLogo asset in share card footer instead of text label - Darken share card gradient background for better contrast - Stories format (9:16) share card with genre and review posters - Lighten statsCardBackground in light theme for subtler contrast - Keep cards at 50% width when only one of genre/review exists Co-authored-by: Cursor <cursoragent@cursor.com>
- Added checks for empty genre lists to conditionally display UI elements in topGenreCard. - Updated layout and styling in ProfileStatsView to enhance readability and visual appeal. - Introduced new text elements for period labels in various views for better context. - Refactored monthContentView and highlightCards functions to streamline logic and improve performance. - Adjusted padding and frame settings for better alignment and responsiveness. These changes aim to create a more engaging and user-friendly experience in the stats section.
…profiles - Added a check to only show the share button when viewing one's own profile, enhancing user experience and preventing unnecessary options for other profiles. - Maintained existing styling and functionality for the share button to ensure consistency across the UI. These changes aim to streamline the user interface and improve the relevance of available actions in the ProfileStatsView.
- Introduced a new service to fetch user stats timeline, allowing for paginated monthly sections of viewing data. - Updated ProfileStatsView and related components to display timeline data, enhancing user engagement with visual summaries of total hours, top genres, and reviews. - Added new query schema and response structure for timeline data, improving data handling and user experience. - Refactored existing components for better organization and readability, ensuring a cohesive integration of the new timeline feature. These changes aim to provide users with a more comprehensive and engaging overview of their viewing habits over time.
- Added new `getUserStatsTimelineService` to retrieve user statistics in a paginated monthly format, providing insights into total hours watched, top genres, and reviews. - Updated `ProfileStatsView` to support timeline mode, displaying monthly sections with improved UI components for better user engagement. - Introduced new query schema for timeline requests and corresponding response schema to structure the data effectively. - Refactored existing services to integrate episode data into total hours calculations, ensuring accurate statistics across different viewing periods. These changes aim to enrich the user experience by offering a more detailed and organized view of user statistics over time.
- Changed cache key for user watched genres from 'watched-genres-v2' to 'watched-genres-v5' for improved versioning. - Introduced a new `GenreItem` type to encapsulate genre-related data, including `tmdbId`, `mediaType`, and `posterPath`. - Refactored genre data structures to include `posterPaths` and `items`, allowing for more detailed genre information in responses. - Updated response schema to accommodate new fields, enhancing the data returned for user watched genres. These changes aim to provide a richer and more organized representation of user genre statistics, improving overall user experience.
…proved response structure - Updated the cache key for user total hours from 'total-hours-v2' to 'total-hours-v6' for better versioning. - Introduced new metrics including peak time slot, hourly distribution, daily activity, and user percentile rank to provide a more comprehensive view of user engagement. - Enhanced the response schema to include these new metrics, improving the data returned for user total hours. - Added new utility functions to compute daily activity and peak viewing times, enriching the analytics capabilities of the service. These changes aim to deliver a more detailed and insightful user statistics experience, enhancing overall engagement and understanding of viewing habits.
…sualization - Updated the task identifier for loading detail data to include dynamic parameters, ensuring accurate data fetching based on user activity. - Refined the logic for determining when to fetch data, focusing on daily activity and hourly distribution metrics. - Introduced a new method to calculate the peak day of the week from daily activity, enhancing the analytics capabilities. - Replaced the peak summary row with a more detailed peak visualization that includes bars representing hourly distribution, improving user engagement and understanding of viewing patterns. These changes aim to provide a more comprehensive and visually appealing representation of user viewing habits in the TimeWatchedDetailView.
Replace the segmented picker (timeline vs general) with a native Menu dropdown that lets users select a specific month or "Geral" (lifetime). Defaults to the current month. Each period loads data on-demand and caches it for instant switching. Co-authored-by: Cursor <cursoragent@cursor.com>
…ced user experience - Introduced PeriodGenresView to display user favorite genres with loading state and genre details. - Added PeriodReviewsView to showcase best reviews for a selected period, including loading state and review details. - Implemented shared components for consistent UI, including detail headers and genre item displays. - Enhanced ProfileStatsSections to integrate new views, improving overall user engagement and data presentation. These additions aim to provide users with a richer and more interactive experience when exploring their viewing habits and preferences.
…iew, and TimeWatchedDetailView - Replaced the previous scrolling detection mechanism with a more efficient approach using scroll offset tracking. - Updated PeriodGenresView and PeriodReviewsView to utilize a new scroll offset reader for improved user experience. - Enhanced TimeWatchedDetailView with similar scroll offset functionality, ensuring consistent behavior across views. - Adjusted ProfileStatsView to set the default selected period to "all" and removed unnecessary animations for smoother transitions. These changes aim to enhance the responsiveness and interactivity of the user interface when navigating through different views.
- Removed unoptimized image loading in the Next.js configuration for improved performance. - Added structured data components including WebsiteJsonLd, OrganizationJsonLd, MovieJsonLd, and BreadcrumbJsonLd to enhance SEO. - Implemented HtmlLangSetter component to dynamically set the document language based on user preferences. - Updated metadata generation in various pages to include language alternates for better search engine indexing. - Refactored existing pages to utilize the new buildLanguageAlternates utility for consistent SEO practices. These changes aim to improve the application's visibility and indexing in search engines, enhancing overall user experience.
…experience - Changed the import statement in next-env.d.ts to use double quotes for consistency. - Added a CSS rule to set the font size of input, select, and textarea elements to 16px on mobile devices, improving usability. These changes aim to ensure a more consistent code style and enhance the user experience on mobile devices.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
|
||
| enum API { | ||
| static let baseURL = Env.apiBaseURL | ||
| static let baseURL = "http://localhost:3333" |
There was a problem hiding this comment.
Hardcoded localhost URL in production iOS app
High Severity
The API base URL was changed from Env.apiBaseURL (which reads from the environment/Info.plist with a production fallback) to a hardcoded "http://localhost:3333". This will cause the iOS app to fail to connect to any backend in non-local environments, breaking all API functionality in production builds.
| return computeDailyBreakdown(movieData, episodes, period) | ||
| } | ||
|
|
||
| const monthCount = period === 'year' ? 12 : 12 |
There was a problem hiding this comment.
Dead conditional always evaluates to same value
Medium Severity
const monthCount = period === 'year' ? 12 : 12 is a no-op ternary — both branches return 12. The old code used 6 months (for (let i = 5; i >= 0; i--)). The else branch at line 128 uses monthCount to generate the month slots for the 'all' period, so the intended distinction between periods is lost. This looks like an incomplete refactor where the non-'year' value was meant to differ (likely 6 to preserve the original behavior).
| eq(schema.reviews.userId, userId), | ||
| sql`${schema.reviews.seasonNumber} IS NULL`, | ||
| sql`${schema.reviews.episodeNumber} IS NULL`, | ||
| ] |
There was a problem hiding this comment.
Best reviews filter for rating=5 silently removed
Medium Severity
selectBestReviews previously filtered reviews with eq(schema.reviews.rating, 5), ensuring only 5-star reviews were returned. This filter was removed entirely without replacement. Now the query returns all reviews (including low-rated ones) ordered by rating descending. While the ordering mitigates the worst case, this fundamentally changes the semantics of "best reviews" and may surface mediocre reviews for users with few high ratings.
| ) { | ||
| const { id } = reviewParamsSchema.parse(request.params) | ||
| const body = updateReviewBodySchema.parse(request.body) | ||
|
|
||
| const result = await updateReviewService({ ...body, id }) | ||
|
|
||
| await invalidateUserStatsCache(redis, request.user.id) |
There was a problem hiding this comment.
Crash accessing request.user.id on unprotected route
High Severity
The newly added invalidateUserStatsCache(redis, request.user.id) in updateReviewController accesses request.user, but the PUT /review/by/:id route definition lacks onRequest: [verifyJwt] middleware. Without JWT verification, request.user is undefined at runtime, so .id throws a TypeError and returns a 500. The old controller never touched request.user, so this crash path is new.
Additional Locations (1)
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 4 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
| day, | ||
| hours: Math.round(hours * 10) / 10, | ||
| })) | ||
| } |
There was a problem hiding this comment.
YYYY-MM period not handled in daily activity computation
Medium Severity
computeAllDailyActivity and computeMonthlyHours don't handle YYYY-MM period strings (e.g., "2024-03"). These fall through to the else branch intended for 'all', causing computeAllDailyActivity to generate daily entries from the earliest date in the (already-filtered) data all the way to today, and computeMonthlyHours to generate 12 months of mostly-empty data. This produces bloated responses for period-scoped timeline requests.
Additional Locations (1)
| const parts = ['user-stats', userId, statType] | ||
| if (language) parts.push(language) | ||
| if (period && period !== 'all') parts.push(period) | ||
| return parts.join(':') |
There was a problem hiding this comment.
Cache key collision between period-scoped and language-scoped stats
Medium Severity
getUserStatsCacheKey appends language and period as positional parts without labels, so a key with language=undefined, period="en-US" would collide with language="en-US", period="all". For example, watched-cast uses language=undefined and period="en-US" (a YYYY-MM looking value aside), while other stat types use language="en-US" and period="all". Since period="all" is filtered out, collisions between unlabeled positional segments are possible.
|
|
||
| currentYM = getPreviousYearMonth(currentYM) | ||
| scanned++ | ||
| } |
There was a problem hiding this comment.
Timeline N+1 sequential calls creates severe latency
High Severity
getUserStatsTimelineService calls three heavy services (getUserTotalHoursService, getUserWatchedGenresService, getUserBestReviewsService) inside a while loop that can iterate up to 24 times. Each service itself does DB queries and external TMDB API calls in batches. On a cold cache, a single timeline request could trigger dozens of DB queries and hundreds of TMDB calls sequentially, leading to extreme response times.
| period: currentMonth, | ||
| totalHours: hoursResponse.totalHours, | ||
| watchedGenres: genres, | ||
| itemsStatus: status, |
There was a problem hiding this comment.
Prefetch omits movie/series hours from cache storage
Medium Severity
The prefetch statsCache.set call omits movieHours, seriesHours, and monthlyHours parameters, so they default to 0, 0, and []. When loadPeriod later reads from this cache, it populates the MonthSection with these zeroed-out values, causing the movie/series hour breakdown and monthly chart to appear empty despite the API having returned valid data in hoursResponse.


Describe your changes
Issue ticket number and link
Checklist before requesting a review
Note
Medium Risk
Adds new period-filtered stats queries and a new timeline endpoint, plus significant iOS stats UI refactor; correctness depends on date-range filtering, cache key/TTL behavior, and additional DB aggregation for percentile calculations.
Overview
Adds period-scoped user stats across the backend by introducing a
periodquery param (includingYYYY-MM) withperiodToDateRange, and threadingstartDate/endDatefiltering through user items, episodes, and best-reviews DB queries.Extends stats caching to include
periodin cache keys and uses a shorter TTL for period-scoped keys; also invalidates user stats caches on review and episode create/update/delete.Expands
total-hoursstats to return additional insights (daily/monthly breakdown adjustments, peak time + hourly distribution, daily activity, and an all-time percentile rank), and adds a newGET /user/:id/stats-timelineendpoint that paginates month sections (hours + top genre + top review).Revamps the iOS Stats tab to consume the new period/timeline APIs: adds period selector + timeline UI, richer genre/review detail screens, share card rendering, new cache keyed by
userId+period, and localized strings/theme tweaks to support the redesigned stats experience.Written by Cursor Bugbot for commit 15bb0f5. This will update automatically on new commits. Configure here.