Remove TOC

This commit is contained in:
2024-08-21 06:37:15 +02:00
parent 426a499300
commit eaf8b5de77
6 changed files with 76 additions and 142 deletions

View File

@ -1,34 +1,34 @@
{% extends "base.html" %} {% extends "base.html" %}
{% block content %} {% block content %}
<div class="container mt-4"> <div class="container mt-4">
<div class="row">
<div class="col-lg-3">{{ toc|safe }}</div>
<div class="col-lg-9">
<div class="row">
{% for game in games %} {% for game in games %}
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-12 mb-4"> <div class="card mb-4 shadow-sm">
<div class="card h-100 shadow-sm"> <div class="row g-0">
<img src="{{ game.image_url }}" <div class="col-md-2">
class="card-img-top" <img src="{{ game.box_art_url }}" alt="{{ game.name }} box art" class="img-fluid rounded-start"
alt="{{ game.display_name }}"> height="283" width="212" loading="lazy">
<div class="card-body d-flex flex-column">
<h5 class="card-title">{{ game.display_name }}</h5>
<div class="mt-auto">
<div class="form-check form-switch">
<input class="form-check-input" type="checkbox" role="switch" id="new">
<label class="form-check-label" for="new">Notify when new</label>
</div> </div>
<div class="form-check form-switch"> <div class="col-md-10">
<input class="form-check-input" type="checkbox" role="switch" id="live"> <div class="card-body">
<label class="form-check-label" for="live">Notify when farmable</label> <h2 class="card-title h5">
<a href="https://www.twitch.tv/directory/category/{{ game.slug }}"
class="text-decoration-none">{{ game.name }}</a>
</h2>
<div>
<a href="" class="text-decoration-none">See previous drops</a>
</div> </div>
<div>
<a href="" class="text-decoration-none">Subscribe to new drops</a>
</div>
<div>
<a href="" class="text-decoration-none">Subscribe to active drops</a>
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div>
</div>
</div>
{% endblock content %} {% endblock content %}

View File

@ -1,30 +1,27 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static campaign_tags game_tags %} {% load static %}
{% load campaign_tags %}
{% load game_tags %}
{% block content %} {% block content %}
<div class="container mt-4"> <div class="container mt-4">
<div class="row">
<div class="col-lg-3">{{ toc|safe }}</div>
<div class="col-lg-9">
{% include "partials/info_box.html" %} {% include "partials/info_box.html" %}
{% include "partials/news.html" %} {% include "partials/news.html" %}
<h2> <h2>
Reward campaign - Reward campaign -
<div class="d-inline text-muted"> <span class="d-inline text-muted">
{{ reward_campaigns.count }} {{ reward_campaigns.count }}
campaign{{ reward_campaigns.count|pluralize }} campaign{{ reward_campaigns.count|pluralize }}
</div> </span>
</h2> </h2>
{% for campaign in reward_campaigns %} {% for campaign in reward_campaigns %}
{% render_campaign campaign %} {# Stored in /core/templatetags/campaign_tags.py #} {% render_campaign campaign %}
{% endfor %} {% endfor %}
<h2> <h2>
Drop campaigns - Drop campaigns -
<div class="d-inline text-muted ">{{ games.count }} game{{ games.count|pluralize }}</div> <span class="d-inline text-muted ">{{ games.count }} game{{ games.count|pluralize }}</span>
</h2> </h2>
{% for game in games %} {% for game in games %}
{% render_game_card game %} {# Stored in /core/templatetags/game_tags.py #} {% render_game_card game %}
{% endfor %} {% endfor %}
</div> </div>
</div>
</div>
{% endblock content %} {% endblock content %}

View File

@ -1,17 +1,14 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %} {% load static %}
{% load campaign_tags %}
{% load game_tags %}
{% block content %} {% block content %}
<div class="container mt-4"> <div class="container mt-4">
<div class="row"> <div class="row">
<div class="col-lg-3">{{ toc }}</div>
<div class="col-lg-9">
<h2>Reward Campaigns</h2> <h2>Reward Campaigns</h2>
<div>
{% for campaign in reward_campaigns %} {% for campaign in reward_campaigns %}
{% include "partials/reward_campaign_card.html" %} {% render_campaign campaign %}
{% endfor %} {% endfor %}
</div> </div>
</div> </div>
</div>
</div>
{% endblock content %} {% endblock content %}

View File

@ -19,14 +19,13 @@ def render_game_card(game: Game) -> SafeText:
Returns: Returns:
The rendered HTML string. The rendered HTML string.
""" """
twitch_id: str = game.twitch_id
box_art_url: str = game.box_art_url or "https://static-cdn.jtvnw.net/ttv-static/404_boxart.jpg" box_art_url: str = game.box_art_url or "https://static-cdn.jtvnw.net/ttv-static/404_boxart.jpg"
name: str = game.name or "Game name unknown" name: str = game.name or "Game name unknown"
slug: str = game.slug or "game-name-unknown" slug: str = game.slug or "game-name-unknown"
drop_campaigns: list[DropCampaign] = game.drop_campaigns.all() # type: ignore # noqa: PGH003 drop_campaigns: list[DropCampaign] = game.drop_campaigns.all() # type: ignore # noqa: PGH003
return format_html( return format_html(
""" """
<div class="card mb-4 shadow-sm" id="#{}"> <div class="card mb-4 shadow-sm">
<div class="row g-0"> <div class="row g-0">
<div class="col-md-2"> <div class="col-md-2">
<img src="{}" alt="{} box art" class="img-fluid rounded-start" height="283" width="212" loading="lazy"> <img src="{}" alt="{} box art" class="img-fluid rounded-start" height="283" width="212" loading="lazy">
@ -45,7 +44,6 @@ def render_game_card(game: Game) -> SafeText:
</div> </div>
</div> </div>
""", """,
twitch_id,
box_art_url, box_art_url,
name, name,
slug, slug,

View File

@ -1,8 +1,7 @@
from __future__ import annotations from __future__ import annotations
import logging import logging
from dataclasses import dataclass from typing import TYPE_CHECKING, Any
from typing import TYPE_CHECKING
import requests_cache import requests_cache
from django.db.models import Prefetch from django.db.models import Prefetch
@ -21,38 +20,6 @@ if TYPE_CHECKING:
logger: logging.Logger = logging.getLogger(__name__) logger: logging.Logger = logging.getLogger(__name__)
@dataclass
class TOCItem:
"""Table of contents item."""
name: str
toc_id: str
def build_toc(list_of_things: list[TOCItem]) -> str:
"""Build the table of contents.
Args:
list_of_things (list[TOCItem]): The list of table of contents items.
Returns:
str: The HTML for the table of contents.
"""
html: str = """
<div class="position-sticky d-none d-lg-block toc">
<div class="card">
<div class="card-body">
<div id="toc-list" class="list-group">
"""
for item in list_of_things:
html += (
f'<a class="list-group-item list-group-item-action plain-text-item" href="#{item.toc_id}">{item.name}</a>'
)
html += """</div></div></div></div>"""
return html
def get_reward_campaigns() -> BaseManager[RewardCampaign]: def get_reward_campaigns() -> BaseManager[RewardCampaign]:
"""Get the reward campaigns. """Get the reward campaigns.
@ -68,32 +35,25 @@ def get_games_with_drops() -> BaseManager[Game]:
Returns: Returns:
BaseManager[Game]: The games with drops. BaseManager[Game]: The games with drops.
""" """
# Filter active drop campaigns active_campaigns_query: BaseManager[DropCampaign] = DropCampaign.objects.filter(ends_at__gte=timezone.now())
active_campaigns: BaseManager[DropCampaign] = DropCampaign.objects.filter(ends_at__gte=timezone.now())
# Prefetch Benefits for each TimeBasedDrop
benefits_prefetch = Prefetch(lookup="benefits", queryset=Benefit.objects.all())
# Filter active time-based drops
active_time_based_drops: BaseManager[TimeBasedDrop] = TimeBasedDrop.objects.filter(ends_at__gte=timezone.now()) active_time_based_drops: BaseManager[TimeBasedDrop] = TimeBasedDrop.objects.filter(ends_at__gte=timezone.now())
# Prefetch Benefits for each active TimeBasedDrop
benefits_prefetch = Prefetch(lookup="benefits", queryset=Benefit.objects.all()) benefits_prefetch = Prefetch(lookup="benefits", queryset=Benefit.objects.all())
# Prefetch TimeBasedDrops for each DropCampaign and include the prefetch of Benefits
time_based_drops_prefetch = Prefetch( time_based_drops_prefetch = Prefetch(
lookup="drops", lookup="drops",
queryset=active_time_based_drops.prefetch_related(benefits_prefetch), queryset=active_time_based_drops.prefetch_related(benefits_prefetch),
) )
# Prefetch DropCampaigns for each Game and include the prefetch of TimeBasedDrops
campaigns_prefetch = Prefetch( campaigns_prefetch = Prefetch(
lookup="drop_campaigns", lookup="drop_campaigns",
queryset=active_campaigns.prefetch_related(time_based_drops_prefetch), queryset=active_campaigns_query.prefetch_related(time_based_drops_prefetch),
) )
# Query the games with the prefetched data return (
return Game.objects.filter(drop_campaigns__in=active_campaigns).prefetch_related(campaigns_prefetch).distinct() Game.objects.filter(drop_campaigns__in=active_campaigns_query)
.distinct()
.prefetch_related(campaigns_prefetch)
.order_by("name")
)
def index(request: HttpRequest) -> HttpResponse: def index(request: HttpRequest) -> HttpResponse:
@ -105,21 +65,19 @@ def index(request: HttpRequest) -> HttpResponse:
Returns: Returns:
HttpResponse: The response object HttpResponse: The response object
""" """
try:
reward_campaigns: BaseManager[RewardCampaign] = get_reward_campaigns() reward_campaigns: BaseManager[RewardCampaign] = get_reward_campaigns()
games: BaseManager[Game] = get_games_with_drops() games: BaseManager[Game] = get_games_with_drops()
tocs: list[TOCItem] = []
for game in games.all():
game_name: str = game.name or "<div class='text-muted'>Game name unknown</div>"
tocs.append(TOCItem(name=game_name, toc_id=f"#{game.twitch_id}"))
toc: str = build_toc(tocs) except Exception:
logger.exception("Error fetching reward campaigns or games.")
return HttpResponse(status=500)
context: dict[str, BaseManager[RewardCampaign] | str | BaseManager[Game]] = { context: dict[str, Any] = {
"reward_campaigns": reward_campaigns, "reward_campaigns": reward_campaigns,
"games": games, "games": games,
"toc": toc,
} }
return TemplateResponse(request=request, template="index.html", context=context) return TemplateResponse(request, "index.html", context)
def game_view(request: HttpRequest) -> HttpResponse: def game_view(request: HttpRequest) -> HttpResponse:

View File

@ -1,16 +0,0 @@
// Create a new ScrollSpy instance that will track scroll position and update the active item in the table of contents
const scrollSpy = new bootstrap.ScrollSpy(document.body, {
target: '.toc' // The target element that contains the table of contents
});
// Listen for the 'activate.bs.scrollspy' event, which is triggered when a new item becomes active in the table of contents
// This is needed because the toc can be longer than our screen and we want to scroll the active item into view
document.body.addEventListener('activate.bs.scrollspy', function (event) {
// Find the currently active item in the table of contents
const activeItem = document.querySelector('.toc .active');
// If an active item is found, scroll it into view smoothly
if (activeItem) {
activeItem.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' });
}
});