Remove TOC
This commit is contained in:
@ -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">
|
{% for game in games %}
|
||||||
<div class="col-lg-3">{{ toc|safe }}</div>
|
<div class="card mb-4 shadow-sm">
|
||||||
<div class="col-lg-9">
|
<div class="row g-0">
|
||||||
<div class="row">
|
<div class="col-md-2">
|
||||||
{% for game in games %}
|
<img src="{{ game.box_art_url }}" alt="{{ game.name }} box art" class="img-fluid rounded-start"
|
||||||
<div class="col-xl-3 col-lg-4 col-md-6 col-sm-12 mb-4">
|
height="283" width="212" loading="lazy">
|
||||||
<div class="card h-100 shadow-sm">
|
</div>
|
||||||
<img src="{{ game.image_url }}"
|
<div class="col-md-10">
|
||||||
class="card-img-top"
|
<div class="card-body">
|
||||||
alt="{{ game.display_name }}">
|
<h2 class="card-title h5">
|
||||||
<div class="card-body d-flex flex-column">
|
<a href="https://www.twitch.tv/directory/category/{{ game.slug }}"
|
||||||
<h5 class="card-title">{{ game.display_name }}</h5>
|
class="text-decoration-none">{{ game.name }}</a>
|
||||||
<div class="mt-auto">
|
</h2>
|
||||||
<div class="form-check form-switch">
|
<div>
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="new">
|
<a href="" class="text-decoration-none">See previous drops</a>
|
||||||
<label class="form-check-label" for="new">Notify when new</label>
|
</div>
|
||||||
</div>
|
<div>
|
||||||
<div class="form-check form-switch">
|
<a href="" class="text-decoration-none">Subscribe to new drops</a>
|
||||||
<input class="form-check-input" type="checkbox" role="switch" id="live">
|
</div>
|
||||||
<label class="form-check-label" for="live">Notify when farmable</label>
|
<div>
|
||||||
</div>
|
<a href="" class="text-decoration-none">Subscribe to active drops</a>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -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">
|
{% include "partials/info_box.html" %}
|
||||||
<div class="col-lg-3">{{ toc|safe }}</div>
|
{% include "partials/news.html" %}
|
||||||
<div class="col-lg-9">
|
<h2>
|
||||||
{% include "partials/info_box.html" %}
|
Reward campaign -
|
||||||
{% include "partials/news.html" %}
|
<span class="d-inline text-muted">
|
||||||
<h2>
|
{{ reward_campaigns.count }}
|
||||||
Reward campaign -
|
campaign{{ reward_campaigns.count|pluralize }}
|
||||||
<div class="d-inline text-muted">
|
</span>
|
||||||
{{ reward_campaigns.count }}
|
</h2>
|
||||||
campaign{{ reward_campaigns.count|pluralize }}
|
{% for campaign in reward_campaigns %}
|
||||||
</div>
|
{% render_campaign campaign %}
|
||||||
</h2>
|
{% endfor %}
|
||||||
{% for campaign in reward_campaigns %}
|
<h2>
|
||||||
{% render_campaign campaign %} {# Stored in /core/templatetags/campaign_tags.py #}
|
Drop campaigns -
|
||||||
{% endfor %}
|
<span class="d-inline text-muted ">{{ games.count }} game{{ games.count|pluralize }}</span>
|
||||||
<h2>
|
</h2>
|
||||||
Drop campaigns -
|
{% for game in games %}
|
||||||
<div class="d-inline text-muted ">{{ games.count }} game{{ games.count|pluralize }}</div>
|
{% render_game_card game %}
|
||||||
</h2>
|
{% endfor %}
|
||||||
{% for game in games %}
|
|
||||||
{% render_game_card game %} {# Stored in /core/templatetags/game_tags.py #}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -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>
|
<h2>Reward Campaigns</h2>
|
||||||
<div class="col-lg-9">
|
{% for campaign in reward_campaigns %}
|
||||||
<h2>Reward Campaigns</h2>
|
{% render_campaign campaign %}
|
||||||
<div>
|
{% endfor %}
|
||||||
{% for campaign in reward_campaigns %}
|
|
||||||
{% include "partials/reward_campaign_card.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
"""
|
"""
|
||||||
reward_campaigns: BaseManager[RewardCampaign] = get_reward_campaigns()
|
try:
|
||||||
games: BaseManager[Game] = get_games_with_drops()
|
reward_campaigns: BaseManager[RewardCampaign] = get_reward_campaigns()
|
||||||
tocs: list[TOCItem] = []
|
games: BaseManager[Game] = get_games_with_drops()
|
||||||
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:
|
||||||
|
@ -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' });
|
|
||||||
}
|
|
||||||
});
|
|
Reference in New Issue
Block a user