Skip to content

Feature: Download Counter and Mod Statistics #93

@AgentKush

Description

@AgentKush

Summary

Add download tracking and display download counts for each mod and tool on the site. Currently, there's no way to know how popular a mod is — users can't see which mods are most downloaded, and mod authors have no visibility into how many times their work has been downloaded. Adding download counters provides social proof, helps users discover popular mods, and gives authors useful feedback.

Current Behavior

  • Mods and tools have download buttons that trigger a Stimulus controller (mods#download)
  • The download action fetches the file from a raw GitHub URL and triggers a browser download
  • No download event is tracked or recorded anywhere
  • No download count is displayed on mod cards or detail pages
  • No way to sort by popularity

Proposed Solution

1. Firestore Download Counter

Add a download_count field to each mod/tool document in Firestore:

mods/{mod_id}:
  name: "My Mod"
  author: "DonovanMods"
  download_count: 0    # <-- new field
  ...

2. Server-Side Download Tracking Endpoint

Create a new API endpoint that the Stimulus controller calls when a download is initiated:

# app/controllers/mods_controller.rb (or a new downloads_controller.rb)

def track_download
  mod_id = params[:id]
  # Increment download_count in Firestore
  mod_ref = firestore.col("mods").doc(mod_id)
  mod_ref.update({ download_count: Google::Cloud::Firestore::FieldValue.increment(1) })
  head :ok
end

Route:

# config/routes.rb
post "/mods/:id/download", to: "mods#track_download", as: :track_mod_download
post "/tools/:id/download", to: "tools#track_download", as: :track_tool_download

3. Update Stimulus Download Controller

Modify the existing mods_controller.js Stimulus controller to fire a tracking request alongside the download:

async download(event) {
  const url = event.params.url
  const fileName = event.params.fileName
  const modId = event.params.id  // <-- new param

  // Track the download (fire and forget — don't block the actual download)
  if (modId) {
    fetch(`/mods/${modId}/download`, { method: "POST", headers: { "X-CSRF-Token": getCSRFToken() } })
  }

  // Existing download logic continues...
  const response = await fetch(url)
  // ...
}

4. Display Download Count on Mod Cards

Show the download count on each mod card in the listing:

<div class="flex items-center gap-1 text-sm text-slate-400">
  <svg class="w-4 h-4"><!-- download icon --></svg>
  <span><%= number_with_delimiter(mod.download_count || 0) %> downloads</span>
</div>

Display formatting:

  • 0-999: Show exact number (e.g., "842 downloads")
  • 1,000-999,999: Show with comma (e.g., "1,234 downloads")
  • 1,000,000+: Show abbreviated (e.g., "1.2M downloads")

5. "Sort by Most Downloaded" Option

Once download counts exist, add a "Most Popular" sort option to the mods page (ties into the search filters enhancement):

@mods = @mods.sort_by { |m| -(m.download_count || 0) }

Files to Modify

File Change
app/controllers/mods_controller.rb Add track_download action with Firestore increment
app/controllers/tools_controller.rb Add track_download action for tools
config/routes.rb Add POST routes for download tracking
app/javascript/controllers/mods_controller.js Add tracking fetch call to download action
app/views/mods/_mod.html.erb Display download count on mod cards
app/views/tools/index.html.erb Display download count on tool cards
app/models/mod.rb Add download_count attribute
app/models/tool.rb Add download_count attribute

Technical Considerations

  • Firestore atomic increment: Use FieldValue.increment(1) to avoid race conditions with concurrent downloads. This is an atomic operation in Firestore — no need to read-then-write.
  • Rate limiting: Consider basic rate limiting on the track endpoint to prevent abuse (e.g., same IP can only increment once per mod per hour). This could be done with a simple in-memory cache or Firestore subcollection.
  • Existing mods: All existing mods will have download_count: nil — handle this with || 0 fallback in views and sorting.
  • CSRF protection: The POST endpoint needs CSRF token validation. The Stimulus controller should include the Rails CSRF token from the meta tag.
  • Analytics: Optionally log download events with timestamp and mod ID for more detailed analytics later (e.g., downloads per day/week charts).

Design Notes

  • Download count displayed in text-slate-400 (subtle, not competing with mod name/author)
  • Small download arrow icon next to the count
  • Position: Below the mod description, above the download button — or inline with the button
  • On hover, could show "X downloads this week" if weekly tracking is added later

Testing

  • Clicking download increments the count in Firestore
  • Download count displays correctly on mod cards
  • Count updates on next page load after downloading
  • Download still works even if tracking endpoint fails (fire and forget)
  • Handles mods with no existing download_count field (nil → 0)
  • Number formatting works correctly (commas, abbreviations)
  • CSRF token is included in tracking request
  • Concurrent downloads don't cause count discrepancies (atomic increment)
  • Sort by "Most Popular" orders mods correctly

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions