Skip to content

feat: Add FL wedge support#23

Merged
masonfox merged 30 commits intomainfrom
feature/fl-wedge-support
Jan 24, 2026
Merged

feat: Add FL wedge support#23
masonfox merged 30 commits intomainfrom
feature/fl-wedge-support

Conversation

@masonfox
Copy link
Owner

Summary

This PR adds comprehensive Freeleech (FL) wedge support to Scurry, allowing users to purchase FL wedges for torrents before downloading. This significantly improves ratio management by making torrents freeleech, so downloads don't count against your ratio.

Closes #21

Features Added

🎯 Core FL Wedge Functionality

  • ✅ New /api/use-wedge endpoint for purchasing FL wedges from MAM
  • ✅ Wedge purchase integration in download flow with proper error handling
  • ✅ User stats cache busting after downloads to reflect updated wedge counts
  • ✅ Ratio impact calculations now account for FL wedges (shows "No Change" instead of negative impact)

🎨 UI Enhancements

  • ✅ Reusable WedgeToggleButton component with lightning bolt icon
  • ✅ FL wedge toggle in sequential search mode
  • ✅ Independent FL wedge controls for both book and audiobook in dual search mode
  • ✅ Freeleech and VIP badges displayed on torrent titles
  • ✅ Hide FL wedge toggles for already-freeleech torrents
  • ✅ Mobile-optimized bottom sheet with separate stats and settings panels
  • ✅ Improved mobile layout with better spacing and keyboard handling

📱 Mobile Improvements

  • ✅ Unified mobile bottom sheet replacing separate stats bar and settings panel
  • ✅ Auto-scroll to books section on dual-mode search
  • ✅ Automatic keyboard dismissal on search submit
  • ✅ Increased bottom padding to prevent overlap
  • ✅ Vertical divider in stats layout for better readability

🖥️ Desktop Improvements

  • ✅ Consolidated dual search layout into integrated 2-row header
  • ✅ External link icon on torrent titles (only icon is clickable)
  • ✅ Entire title line now clickable for better UX
  • ✅ Improved spacing between FL wedge toggle and download button

🧪 Test Coverage

  • ✅ Comprehensive test suite with 194 total tests (40 new tests added)
  • 95.43% statement coverage (was 75.57%)
  • 94.09% branch coverage (was 72.15%)
  • 98.56% line coverage (was 77.33%)
  • ✅ New test files:
    • __tests__/user-stats.test.mjs (22 tests)
    • __tests__/message-banner.test.jsx (8 tests)
    • __tests__/use-wedge.test.mjs (11 tests)
  • ✅ Enhanced existing tests for add route, search route, and utilities
  • ✅ React Testing Library integration for component testing

🔧 Infrastructure

  • ✅ Test coverage reporting with Vitest
  • ✅ PR checks workflow includes coverage validation
  • ✅ React Testing Library dependencies installed
  • ✅ JSX test file support configured

Technical Details

New API Endpoints

  • POST /api/use-wedge - Purchase FL wedge for a torrent
    • Validates torrent ID
    • Calls MAM bonus buy API
    • Handles token expiration
    • Returns success/error status

Cache Management

  • User stats cache is automatically busted after successful downloads
  • Ensures wedge counts and stats are always fresh
  • 30-minute TTL with automatic cleanup for >100 entries

Error Handling

  • Comprehensive error handling for wedge purchase failures
  • Token expiration detection and user feedback
  • Graceful fallbacks for missing data
  • Proper HTTP status codes (401, 400, 502, 500)

Ratio Calculations

  • calculateNewRatio() and calculateRatioDiff() account for FL wedges
  • Shows "No Change" when FL wedge is used (since download doesn't count)
  • Maintains 4 decimal precision for accurate ratio tracking

Testing

All tests pass with excellent coverage:

Test Files: 16 passed (16)
Tests: 194 passed (194)
Coverage:
- Statements: 95.43%
- Branches: 94.09%
- Lines: 98.56%

Run tests locally:

npm test
npm test -- --coverage

Breaking Changes

None - this is a purely additive feature.

Screenshots

(Would include screenshots of the FL wedge UI in actual PR)

Checklist

  • ✅ Code follows project conventions
  • ✅ All tests pass
  • ✅ Test coverage significantly improved (95%+)
  • ✅ No breaking changes
  • ✅ Documentation updated (commit messages are detailed)
  • ✅ Mobile and desktop UX tested
  • ✅ Error handling comprehensive

- Add FL wedge count display in user stats bar
- Implement wedge purchase API endpoint (/api/use-wedge)
- Add clickable wedge toggle icons for single mode downloads
- Add independent wedge toggles for dual mode (audiobook + book)
- Update download API to support useWedge parameter
- Abort download if wedge purchase fails
- Auto-refresh stats after wedge usage
- Add utility functions for torrent ID extraction
- Refactor search result titles to be clickable links
- Remove separate 'View' button for cleaner UI

Closes #21
…olt icon

- Create reusable WedgeToggleButton component
- Support small (icon-only) and large (icon+label) sizes
- Replace medal icon with lightning bolt for better symbolism
- Update tooltip to 'Use Freeleech Wedge' for clarity
- Add cursor-pointer and amber-400 active color
- Reduce code duplication across single and dual modes
- Create MobileBottomSheet component combining progress and download
- Use single fixed container to avoid stacking conflicts
- Separate wedge selector into its own panel (matches progress style)
- Remove DualDownloadButton from mobile flow
- Elements stack naturally with proper spacing
- Progress shows during steps 1-2, wedges show when both selected
- Cleaner mobile UX with consistent panel styling
Replace horizontal layout with 2x2 grid featuring a vertical divider down the center, improving visual separation between upload/ratio stats and download/FL wedge stats on mobile devices
Blur the search input field when the form is submitted to automatically dismiss the mobile keyboard, improving the mobile user experience by clearing screen space for search results
When search results load in dual mode on mobile, automatically scroll to the books section so users can immediately see results without manual scrolling. Also scroll to audiobooks section when a book is selected to maintain the two-step flow
…eet overlap

Increase bottom padding from pb-32 to pb-40 to ensure both selected items are fully visible above the mobile bottom sheet
Add a subtle gray external link icon next to torrent titles to make it more clear that they are clickable links that open in a new tab
Move author and VIP badge inside the link to increase clickable area. Move external link icon to end of line and adjust spacing for better visual balance
- Add freeleech indicator using MAM's official freedownload.gif
- Move VIP badge before title (previously after)
- Download and use local badge assets instead of remote URLs
- Parse 'free' field from MAM API response
- Display order: [VIP] [Freeleech] Title
- Update PropTypes and test fixtures
- Don't show wedge toggle in single mode if torrent is freeleech
- Hide wedge toggles in dual mode for freeleech items
- Hide entire wedge panel if both selected items are freeleech
- Pass selected items to MobileBottomSheet to check freeleech status
- Prevents wasting FL wedges on already-freeleech torrents
- Remove anchor tag wrapping entire title/author line
- Make only the external link icon clickable for MAM link
- Add stopPropagation to prevent conflicts with selectable items
- Improves dual-fetch UX: easier to select items without accidentally clicking link
- Especially helpful on mobile where title line was hard to tap
- Install @vitest/coverage-v8 for code coverage reporting
- Configure coverage thresholds (60% for lines, functions, branches, statements)
- Exclude UI components and focus on API routes and utilities
- Generate HTML, LCOV, JSON, and text coverage reports
- Current coverage: 63% overall, 87% for src/lib utilities
- Add npm run test:coverage command
- Set "type": "module" in package.json
- Eliminates MODULE_TYPELESS_PACKAGE_JSON warning
- Improves performance by avoiding CommonJS reparsing
- Change test command to test:coverage to generate coverage reports
- Explicitly specify lcov.info file for Codecov upload
- Use fail_ci_if_error instead of continue-on-error for clarity
- Now actually generates coverage data for Codecov badge
- Change test command to test:coverage to generate coverage reports
- Explicitly specify lcov.info file for Codecov upload
- Ensures coverage reports are uploaded on main branch pushes
- Consistent with PR checks workflow
- Reorder mobile layout: FL wedge (left) | ratio (center) | download (right)
- Use justify-between to spread elements across full width on mobile
- Center ratio text with flex-1 and text-center
- Desktop layout unchanged: wedge + download together, ratio below
- Improves mobile UX with better visual balance
- Change gap from gap-2 to gap-2 md:gap-3
- Mobile keeps gap-2, desktop uses gap-3 (12px)
- Provides more breathing room between controls on larger screens
Instead of extracting torrentId from downloadUrl in the backend, pass it
directly from the frontend where it's already available. This simplifies
the API and removes the need for URL parsing.
Add bustStatsCache function to invalidate cached user stats when torrents
are added, ensuring stats are refreshed to reflect new downloads. Only
bust cache on successful downloads, not on failures.
Add test coverage for FL wedge purchase functionality including:
- Input validation (missing/invalid torrentId)
- Successful wedge purchases
- Various error conditions (API failures, expired tokens, invalid responses)
- Edge cases (numeric torrentId, invalid JSON responses)
When FL wedge is enabled or torrent is already freeleech, ratio impact
now shows 'Same' instead of negative impact. For dual downloads, only
non-freeleech items are included in ratio calculations.
- Add comprehensive test suite for user-stats route (22 tests)
  - Cache hit/miss scenarios
  - Token expiration detection (403 & HTML responses)
  - Invalid JSON handling
  - Missing data field defaults
  - Cache cleanup for >100 entries
  - All error paths covered

- Add MessageBanner component tests (8 tests)
  - All message types (error, success, info)
  - CSS class application and styling
  - Icon rendering verification

- Enhance add route tests (6 new wedge integration tests)
  - Successful wedge purchase flow
  - Failed wedge purchase error handling
  - Cache busting behavior
  - Error propagation with wedgeFailed flag

- Expand utilities tests
  - maskToken edge cases (tokens 6-10 chars, non-string types)
  - validateMamToken regex branch coverage
  - generateTimestamp unit test

- Add search route test for missing/null fields
- Add use-wedge test for missing error message fallback

- Install React Testing Library dependencies
  - @testing-library/react
  - @testing-library/jest-dom
  - @vitejs/plugin-react
  - jsdom

- Update Vitest config
  - Add React plugin support
  - Change environment to jsdom
  - Include .jsx test files

Coverage improvements:
- Statements: 75.57% → 95.43%
- Branches: 72.15% → 94.09%
- Lines: 77.33% → 98.56%
- Total tests: 154 → 194 (40 new tests)
- All tests passing ✓
@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

Files with missing lines Coverage Δ
app/api/add/route.js 100.00% <100.00%> (ø)
app/api/search/route.js 100.00% <ø> (ø)
app/api/use-wedge/route.js 100.00% <100.00%> (ø)
app/api/user-stats/route.js 100.00% <100.00%> (ø)
src/lib/utilities.js 90.76% <100.00%> (-9.24%) ⬇️

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@masonfox masonfox changed the title feat: Add FL wedge support with comprehensive test coverage feat: Add FL wedge support Jan 23, 2026
@masonfox masonfox merged commit 18be7b2 into main Jan 24, 2026
3 checks passed
@masonfox masonfox deleted the feature/fl-wedge-support branch January 24, 2026 13:16
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.

FL Wedge Support

1 participant

Comments