diff --git a/templates/base.html b/templates/base.html
index c6137e3..7a3c9e4 100644
--- a/templates/base.html
+++ b/templates/base.html
@@ -47,7 +47,11 @@
th, td { padding: 8px; text-align: left; vertical-align: middle; }
th {background-color: Canvas; color: CanvasText; font-weight: bold; }
tr:nth-child(even) { background-color: color-mix(in srgb, Canvas 95%, CanvasText 5%); }
- td img { display: block; height: 160px; width: 160px; object-fit: cover; border-radius: 4px; }
+ td > img { display: block; height: 160px; width: 160px; object-fit: cover; border-radius: 4px; }
+
+ .campaign-benefits { margin-top: 4px; display: flex; flex-wrap: wrap; gap: 8px; }
+ .benefit-item { display: inline-flex; align-items: center; padding: 2px 6px; background-color: color-mix(in srgb, Canvas 90%, CanvasText 10%); border-radius: 4px; font-size: 0.9em; }
+ .benefit-item img { width: 24px !important; height: 24px !important; display: inline-block !important; margin-right: 4px; border-radius: 2px; }
@media (prefers-color-scheme: dark) {
.highlight { background: #0d1117; color: #E6EDF3; }
diff --git a/templates/twitch/game_detail.html b/templates/twitch/game_detail.html
index 6a2c8aa..bc07e9a 100644
--- a/templates/twitch/game_detail.html
+++ b/templates/twitch/game_detail.html
@@ -53,6 +53,25 @@
{{ campaign.clean_name }}
+ {% if campaign.time_based_drops.all %}
+
+ {% comment %}Show unique benefits sorted alphabetically{% endcomment %}
+ {% for benefit in campaign.sorted_benefits %}
+
+ {% if benefit.image_asset_url %}
+
+ {% endif %}
+ {{ benefit.name }}
+
+ {% endfor %}
+
+ {% endif %}
|
Ends in {{ campaign.end_at|timeuntil }}
@@ -70,6 +89,24 @@
|
{{ campaign.clean_name }}
+ {% if campaign.time_based_drops.all %}
+
+ {% for benefit in campaign.sorted_benefits %}
+
+ {% if benefit.image_asset_url %}
+
+ {% endif %}
+ {{ benefit.name }}
+
+ {% endfor %}
+
+ {% endif %}
|
Starts in {{ campaign.start_at|timeuntil }}
@@ -87,6 +124,25 @@
|
{{ campaign.clean_name }}
+ {% if campaign.time_based_drops.all %}
+
+ {% comment %}Show unique benefits sorted alphabetically{% endcomment %}
+ {% for benefit in campaign.sorted_benefits %}
+
+ {% if benefit.image_asset_url %}
+
+ {% endif %}
+ {{ benefit.name }}
+
+ {% endfor %}
+
+ {% endif %}
|
{{ campaign.end_at|timesince }} ago
diff --git a/twitch/views.py b/twitch/views.py
index 09a5ed0..14abb08 100644
--- a/twitch/views.py
+++ b/twitch/views.py
@@ -438,7 +438,16 @@ class GameDetailView(DetailView):
subscription = NotificationSubscription.objects.filter(user=user, game=game).first()
now: datetime.datetime = timezone.now()
- all_campaigns: QuerySet[DropCampaign, DropCampaign] = DropCampaign.objects.filter(game=game).select_related("game__owner").order_by("-end_at")
+ all_campaigns: QuerySet[DropCampaign, DropCampaign] = (
+ DropCampaign.objects.filter(game=game)
+ .select_related("game__owner")
+ .prefetch_related(
+ Prefetch(
+ "time_based_drops", queryset=TimeBasedDrop.objects.prefetch_related(Prefetch("benefits", queryset=DropBenefit.objects.order_by("name")))
+ )
+ )
+ .order_by("-end_at")
+ )
active_campaigns: list[DropCampaign] = [
campaign
@@ -453,6 +462,15 @@ class GameDetailView(DetailView):
expired_campaigns: list[DropCampaign] = [campaign for campaign in all_campaigns if campaign.end_at is not None and campaign.end_at < now]
+ # Add unique sorted benefits to each campaign object
+ for campaign in all_campaigns:
+ benefits_dict = {} # Use dict to track unique benefits by ID
+ for drop in campaign.time_based_drops.all(): # type: ignore[attr-defined]
+ for benefit in drop.benefits.all():
+ benefits_dict[benefit.id] = benefit
+ # Sort benefits by name and attach to campaign
+ campaign.sorted_benefits = sorted(benefits_dict.values(), key=lambda b: b.name) # type: ignore[attr-defined]
+
serialized_game = serialize(
"json",
[game],
|