Skip to content

feat(web): right-click context menu, standalone hero carousel, instant tab navigation & UI layout fixes - #501

Open
Himanth-reddy wants to merge 16 commits into
ProdigyV21:mainfrom
Himanth-reddy:feature/web-right-click-context-menu
Open

feat(web): right-click context menu, standalone hero carousel, instant tab navigation & UI layout fixes#501
Himanth-reddy wants to merge 16 commits into
ProdigyV21:mainfrom
Himanth-reddy:feature/web-right-click-context-menu

Conversation

@Himanth-reddy

Copy link
Copy Markdown
Collaborator

Summary of Changes

This PR introduces a right-click context menu, converts the home hero banner into an auto-rotating carousel, resolves UI performance/lag bottlenecks on navigation and hover, and fixes layout clipping and alignment issues across the web app.


Detailed Breakdown of Issues, Causes, and Solutions

1. Standalone Auto-Rotating Hero Carousel & Hover Lag Elimination

  • Problem & Cause: Sweeping the mouse cursor rapidly across media cards in catalog rails triggered immediate onMouseEnter prefetch API calls and hero preview state updates for every passed card. Sweeping over 10–15 cards in a second spawned 30+ network requests and React state updates, saturating the main thread and causing noticeable cursor lag and UI stutter.
  • Solution:
    • Decoupled rail card hover from the hero banner. Replaced mouse-driven hero preview swaps with an autonomous, auto-rotating hero carousel that advances every 6.5s (pausing on hover).
    • Added a 120ms hover intent debounce timer (MediaCard.tsx) so fast cursor sweeps across rails produce zero API calls or state thrashing.
    • Added manual indicator dots positioned on the desktop right side, plus touch swipe gesture support (onTouchStart/onTouchEnd with a 50px threshold) for mobile.

2. Instant Tab Navigation & Details Return (0ms Delay)

  • Problem & Cause:
    1. Switching between navigation tabs (Home, Search, Watchlist, Live TV, Settings) conditionally unmounted <HomeScreen /> completely ({section === "home" && <HomeScreen />}). Re-entering Home forced component re-initialization, catalog state re-seeding, and network calls.
    2. Opening the item details view (selected ? <DetailsDrawer /> : <...screens...>) completely unmounted all active tab screens. Closing Details forced <HomeScreen /> to re-mount from scratch, creating a ~1s loading delay.
    3. useEffect in HomeScreen blocked setting displayHero until background title-treatment logo PNG/SVG requests (getLogoUrl) finished over the network.
  • Solution:
    • Maintained warm tab DOM instances in AppShell.tsx using CSS display toggling (<div style={{ display: !selected && section === "home" ? "contents" : "none" }}>). Returning to Home or closing Details is now 100% instantaneous (0ms) with zero re-fetching or re-mounting.
    • Updated HomeScreen to synchronously render displayHero immediately while getLogoUrl fetches asynchronously in the background.

3. Fixed Hero Overview Text Clipping & Vertical Layout Shifts ("Going Up & Down")

  • Problem & Cause:
    1. .hero-overview used a rigid pixel height: clamp(44px, 5.2vh, 60px) !important; combined with line-clamping and JS character truncation (desc.slice(0, 150)). On various viewport heights, line-height misalignments caused the 2nd/3rd line of text to be cut off horizontally ("tucked under").
    2. When height was auto, overview descriptions with 1 or 2 lines shrank the text box height. This caused the Play & More Info buttons (.hero-actions) to jump up and down vertically when carousel slides auto-rotated between titles with different text lengths.
  • Solution:
    • Removed hardcoded JS character slicing (desc.slice(0, 150)).
    • Set min-height: calc(3 * 1.45em) !important; height: calc(3 * 1.45em) !important; on desktop (calc(2 * 1.45em) on mobile).
    • Reserved a fixed 3-line box height at all times so text is never clipped mid-line and Play/More Info buttons stay 100% vertically locked without layout jumping.

4. Live TV Screen Top Clearance

  • Problem & Cause: .screen.livetv-shell was targeted by a padding-top: 0 CSS rule, causing the Live TV heading, search input, and playlist buttons to render at pixel 0 directly underneath the fixed top navigation bar.
  • Solution: Removed .screen.livetv-shell from padding-top: 0 and set padding-top: clamp(112px, 14.3vh, 154px) in globals.css, aligning Live TV top clearance with Home, Watchlist, Search, and Browse screens.

5. GPU-Accelerated Shimmer & Skeleton Placeholders

  • Problem & Cause: Card skeletons and loading poster placeholders used background-position gradient animations, causing frequent browser layout re-paints and scroll stutter.
  • Solution: Upgraded loading shimmers to hardware-accelerated transform: translateX() compositor animations (will-change: transform) running smoothly at 60/120fps.

6. PC Right-Click & Mobile Long-Press Context Menu

  • Problem & Cause: Desktop users had no direct right-click context menu for media items, requiring opening the full details drawer to access actions like bookmarking or marking watched.
  • Solution: Implemented MediaContextMenu component and attached PC right-click (onContextMenu) and mobile long-press handlers across media cards.

Verification

  • Tested tab switching speed across Home, Search, Watchlist, Live TV, and Settings (instantaneous 0ms switch).
  • Verified opening and closing DetailsDrawer maintains Home screen state with zero reload delay.
  • Tested hero carousel auto-rotation, manual dot navigation, and touch swiping.
  • Verified hero text layout stability across various desktop and mobile screen resolutions.
  • Verified TypeScript build passes (npx tsc --noEmit).

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 473c244bd8

ℹ️ About Codex in GitHub

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

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

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

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

Comment thread web/components/shell/AppShell.tsx Outdated
<div style={{ display: !selected && section === "home" ? "contents" : "none" }}><HomeScreen /></div>
<div style={{ display: !selected && section === "search" ? "contents" : "none" }}><SearchScreen /></div>
<div style={{ display: !selected && section === "watchlist" ? "contents" : "none" }}><WatchlistScreen /></div>
<div style={{ display: !selected && section === "tv" ? "contents" : "none" }}><LiveTvScreen /></div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate Live TV mounting until the tab is visible

Because this now mounts <LiveTvScreen /> even while it is hidden, its mount effect in LiveTvScreen.tsx lines 88-96 calls refreshIptv() whenever playlists exist and the snapshot is stale. For users with IPTV configured, simply opening Home now starts the expensive playlist refresh/parsing path in the background even if they never visit TV, regressing startup/home responsiveness; keep this screen lazy or pass visibility so the refresh only runs when the TV tab is active.

Useful? React with 👍 / 👎.

type="button"
className={`context-menu-action-btn ${act.danger ? "is-danger" : ""}`}
onClick={() => {
act.action();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Close custom context-menu actions before running them

This only invokes the supplied action, while the custom actions added for episode/season menus and Live TV rows do not call closeContextMenu() themselves. In those contexts, choosing actions such as Play Channel or Play Episode leaves the z-index 2000 scrim/card open over the player or source picker until the user dismisses it manually, unlike the default media actions which close first.

Useful? React with 👍 / 👎.

Comment thread web/lib/store.tsx
markWatchedLocally({ mediaType: item.mediaType, id: item.id, season: seasonNumber, episode: episodeNumber }, !currentlyWatched);
setToast(!currentlyWatched ? "Marked as watched." : "Marked as unwatched.");

if (activeSyncProvider() !== "none") {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Persist context-menu watched changes to ARVIO Cloud

When a user is signed into ARVIO Cloud but has no Trakt/MDBList provider connected, this branch is skipped after only markWatchedLocally() runs, so the context-menu “Mark as watched” toast succeeds but the change is lost on refresh/reload and Cloud continue-watching can reappear. The existing detail-page flow persists the same action through saveProgress(...); this shared context-menu path needs the same Cloud write or it should not report a durable watched update.

Useful? React with 👍 / 👎.

@ProdigyV21 ProdigyV21 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Thanks Himanth — there's real value here: the right-click/long-press context menu framework (pointer-only, so TV nav is untouched), the IPTV sort-order setting, and the skeleton/shimmer polish are all genuine improvements.

But this bundles 4+ features, and several collide with what landed on main this week:

  1. Watched/Continue-Watching sync corrupts data. toggleWatched passes progress: 1 to saveProgress, but the cloud payload uses a 0–100 scale — cloud.ts only drops an item from Continue Watching at progress >= 90, otherwise it inserts it at the TOP. "Mark as watched" therefore pushes the title into Continue Watching at 1% on every device, including the Android app. removeFromContinueWatching also never touches the cloud payload, so removed items resurface on the next refresh.

  2. The Live TV padding hunk re-breaks a landed fix. Main fixed the top-bar overlap at a measured clamp(84px, 9vh, 108px) (e153ff58); this PR's 112–154px predates that and adds ~40px of dead space. Drop the hunk.

  3. The always-mounted AppShell is too costly on TV browsers (our primary target): all six screens mount at boot and hidden screens keep working — the hero keeps rotating and fetching logos/ratings forever. The display: contents wrappers also break the .content > .screen rule.

  4. The hero rework regresses main's behavior: previous title's logo shows during swaps (main synchronizes those on purpose), hover/focus-to-pin is removed, and the 12 dots become D-pad focus stops.

  5. Please remove: version.json (build-generated, never commit), the TMDB host swap, the unused plugin/scraper types and settings fields, and the logo→PNG swaps.

Could you split it? PR 1: context menu with fixed watched/CW sync (+ role="dialog" on the scrim, CSS for the header-art/footer classes). PR 2: IPTV sort order — nearly mergeable as-is. PR 3: skeletons/shimmer. I'd gladly take all three.

@Himanth-reddy

Copy link
Copy Markdown
Collaborator Author

Thanks for the detailed review!

I've updated this PR to focus specifically on Context Menu & Watched/CW Sync:

  1. Fixed Watched & Continue Watching Sync: toggleWatched now passes progress: 100 so cloud.ts recognizes completion (progress >= 90) and drops the item from Continue Watching instead of placing it at 1%. removeFromContinueWatching also updates the cloud payload to prevent removed items from resurfacing.
  2. Context Menu Accessibility & CSS: Added role="dialog" and aria-modal="true" to the context menu scrim, along with .context-menu-header-art and .context-menu-footer-hint CSS classes.
  3. Reverted Unrelated Features: Reverted the always-mounted AppShell, standalone hero carousel rework, Live TV padding change, version.json, TMDB proxy change, and unused types/settings fields.

Regarding point 3 (AppShell always-mounted for TV browsers) — why would people use the web app in a TV's browser if there is already the native app?

Copy link
Copy Markdown
Owner

Thanks for cleaning up the scope. The context-menu feature is a useful addition, and the current branch merges cleanly into main. Both GitHub CI and a local merged web production build pass.

There are still a few behavior issues to fix before merging:

  1. Remove from Continue Watching calls the Trakt/MDBList remove-from-history API. This can erase watched history, while the provider's paused-playback entry is not removed, so the item may return after refresh. Please use a dedicated dismissal/playback-removal path without changing watched history.
  2. Season watched/unwatched actions iterate the episodes loaded for the currently selected season, even when a different season tab was right-clicked. They are also local-only and disappear after refresh. Load the clicked season's episodes and persist the result through cloud/Trakt/MDBList.
  3. Mobile long-press can still trigger the card's normal click when the finger is released, opening Details behind the menu. Track when the long-press fires and suppress the following click.
  4. saveProgress expects progress from 0 to 1, but the new actions pass 100. Please use 1 for completed progress.
  5. Play and View Details currently do the same thing: both call openDetails(item). Please wire Play to the playback/source flow, or remove/rename the duplicate action.

Minor cleanup: remove the trailing whitespace in AppShell.tsx and update the PR title/description, since the carousel and always-mounted tab changes are no longer part of the final six-file diff.

Once these points are addressed, this should be in much better shape to merge.

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