Add breadcrumb navigation
All checks were successful
Deploy to Server / deploy (push) Successful in 20s

This commit is contained in:
Joakim Hellsén 2026-03-22 05:09:27 +01:00
commit 428f49879d
Signed by: Joakim Hellsén
SSH key fingerprint: SHA256:/9h/CsExpFp+PRhsfA0xznFx2CGfTT5R/kpuFfUgEQk
28 changed files with 350 additions and 416 deletions

View file

@ -289,9 +289,6 @@ def campaign_detail_view(request: HttpRequest, kick_id: str) -> HttpResponse:
campaign.rewards.order_by("required_units").select_related("category"), # type: ignore[union-attr] campaign.rewards.order_by("required_units").select_related("category"), # type: ignore[union-attr]
) )
channels: list[KickChannel] = list(campaign.channels.select_related("user")) channels: list[KickChannel] = list(campaign.channels.select_related("user"))
reward_count: int = len(rewards)
channels_count: int = len(channels)
total_watch_minutes: int = sum(reward.required_units for reward in rewards)
breadcrumb_schema: str = _build_breadcrumb_schema([ breadcrumb_schema: str = _build_breadcrumb_schema([
{"name": "Home", "url": request.build_absolute_uri("/")}, {"name": "Home", "url": request.build_absolute_uri("/")},
@ -360,9 +357,6 @@ def campaign_detail_view(request: HttpRequest, kick_id: str) -> HttpResponse:
"campaign": campaign, "campaign": campaign,
"rewards": rewards, "rewards": rewards,
"channels": channels, "channels": channels,
"reward_count": reward_count,
"channels_count": channels_count,
"total_watch_minutes": total_watch_minutes,
"now": now, "now": now,
"breadcrumb_schema": breadcrumb_schema, "breadcrumb_schema": breadcrumb_schema,
**seo_context, **seo_context,

View file

@ -114,7 +114,6 @@
{% for channel in campaign_data.allowed_channels|slice:":5" %} {% for channel in campaign_data.allowed_channels|slice:":5" %}
<li style="margin-bottom: 0.1rem;"> <li style="margin-bottom: 0.1rem;">
<a href="https://twitch.tv/{{ channel.name }}" <a href="https://twitch.tv/{{ channel.name }}"
rel="nofollow ugc"
title="Watch {{ channel.display_name }} on Twitch"> title="Watch {{ channel.display_name }} on Twitch">
{{ channel.display_name }}</a><a href="{% url 'twitch:channel_detail' channel.twitch_id %}" {{ channel.display_name }}</a><a href="{% url 'twitch:channel_detail' channel.twitch_id %}"
title="View {{ channel.display_name }} details" title="View {{ channel.display_name }} details"
@ -126,7 +125,6 @@
{% if campaign_data.campaign.game.twitch_directory_url %} {% if campaign_data.campaign.game.twitch_directory_url %}
<li> <li>
<a href="{{ campaign_data.campaign.game.twitch_directory_url }}" <a href="{{ campaign_data.campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled"> title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled">
Go to a participating live channel Go to a participating live channel
</a> </a>
@ -144,7 +142,6 @@
{% if campaign_data.campaign.game.twitch_directory_url %} {% if campaign_data.campaign.game.twitch_directory_url %}
<li> <li>
<a href="{{ campaign_data.campaign.game.twitch_directory_url }}" <a href="{{ campaign_data.campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled"> title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled">
Go to a participating live channel Go to a participating live channel
</a> </a>
@ -321,7 +318,7 @@
{% if campaign_data.channels %} {% if campaign_data.channels %}
{% for channel in campaign_data.channels|slice:":5" %} {% for channel in campaign_data.channels|slice:":5" %}
<li style="margin-bottom: 0.1rem;"> <li style="margin-bottom: 0.1rem;">
<a href="{{ channel.channel_url }}" rel="nofollow ugc" target="_blank"> <a href="{{ channel.channel_url }}">
{% if channel.user %} {% if channel.user %}
{{ channel.user.username }} {{ channel.user.username }}
{% else %} {% else %}

View file

@ -41,7 +41,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Latest Twitch drop campaigns</td> <td>Latest Twitch drops</td>
<td> <td>
<a href="https://ttvdrops.lovinator.space/rss/campaigns/">https://ttvdrops.lovinator.space/rss/campaigns/</a> <a href="https://ttvdrops.lovinator.space/rss/campaigns/">https://ttvdrops.lovinator.space/rss/campaigns/</a>
</td> </td>
@ -77,7 +77,7 @@
</td> </td>
</tr> </tr>
<tr> <tr>
<td>Latest Kick campaigns</td> <td>Latest Kick drops</td>
<td> <td>
<a href="https://ttvdrops.lovinator.space/kick/rss/campaigns/">https://ttvdrops.lovinator.space/kick/rss/campaigns/</a> <a href="https://ttvdrops.lovinator.space/kick/rss/campaigns/">https://ttvdrops.lovinator.space/kick/rss/campaigns/</a>
</td> </td>

View file

@ -20,11 +20,6 @@
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<main> <main>
<nav aria-label="Breadcrumb">
<a href="{% url 'kick:dashboard' %}">Kick</a> &gt;
<a href="{% url 'kick:campaign_list' %}">Campaigns</a> &gt;
{{ campaign.name }}
</nav>
<div style="display: flex; <div style="display: flex;
align-items: flex-start; align-items: flex-start;
gap: 1.5rem; gap: 1.5rem;
@ -51,45 +46,30 @@
</div> </div>
<div> <div>
<h1 style="margin: 0 0 0.25rem 0;">{{ campaign.name }}</h1> <h1 style="margin: 0 0 0.25rem 0;">{{ campaign.name }}</h1>
{% if campaign.category %}
<div> <div>
<a href="{% url 'kick:game_campaign_feed' campaign.category.kick_id %}" <a href="{% url 'kick:dashboard' %}">Kick</a> >
title="RSS feed for {{ campaign.category.name }} campaigns">[rss]</a> <a href="{% url 'kick:campaign_list' %}">Campaigns</a> >
<a href="{% url 'kick:game_campaign_feed_atom' campaign.category.kick_id %}" {{ campaign.name }}
title="Atom feed for {{ campaign.category.name }} campaigns">[atom]</a>
<a href="{% url 'kick:game_campaign_feed_discord' campaign.category.kick_id %}"
title="Discord feed for {{ campaign.category.name }} campaigns">[discord]</a>
<a href="{% url 'core:docs_rss' %}" title="RSS feed documentation">[explain]</a>
</div> </div>
{% endif %} <div>
<p style="margin: 0.25rem 0; color: #666;">
Status: {{ campaign.status|default:"unknown"|capfirst }}
{% if campaign.rule_name %}- Rule: {{ campaign.rule_name }}{% endif %}
</p>
{% if campaign.category or campaign.organization %}
<p style="margin: 0.25rem 0;">
{% if campaign.category %} {% if campaign.category %}
Category:
<a href="{% url 'kick:game_detail' campaign.category.kick_id %}">{{ campaign.category.name }}</a> <a href="{% url 'kick:game_detail' campaign.category.kick_id %}">{{ campaign.category.name }}</a>
{% endif %} {% endif %}
</div>
<div>
{% if campaign.organization %} {% if campaign.organization %}
{% if campaign.category %}-{% endif %} Organization:
<a href="{% url 'kick:organization_detail' campaign.organization.kick_id %}">{{ campaign.organization.name }}</a> <a href="{% url 'kick:organization_detail' campaign.organization.kick_id %}">{{ campaign.organization.name }}</a>
{% endif %} {% endif %}
</p> </div>
{% endif %}
<p style="margin: 0.25rem 0; font-size: 0.85rem; color: #666;">
ID: {{ campaign.kick_id }}
{% if campaign.rule_id %}- Rule ID: {{ campaign.rule_id }}{% endif %}
- Added: <time datetime="{{ campaign.added_at|date:'c' }}"
title="{{ campaign.added_at|date:'DATETIME_FORMAT' }}">{{ campaign.added_at|date:"M d, Y" }}</time>
({{ campaign.added_at|timesince }} ago)
- Updated: <time datetime="{{ campaign.updated_at|date:'c' }}"
title="{{ campaign.updated_at|date:'DATETIME_FORMAT' }}">{{ campaign.updated_at|date:"M d, Y" }}</time>
({{ campaign.updated_at|timesince }} ago)
</p>
{% if campaign.ends_at %} {% if campaign.ends_at %}
<p style="margin: 0.25rem 0;"> <div>
<strong>Ends:</strong> {% if campaign.ends_at > now %}
Ends
{% else %}
Ended at
{% endif %}
<time datetime="{{ campaign.ends_at|date:'c' }}" <time datetime="{{ campaign.ends_at|date:'c' }}"
title="{{ campaign.ends_at|date:'DATETIME_FORMAT' }}">{{ campaign.ends_at|date:"M d, Y H:i" }}</time> title="{{ campaign.ends_at|date:'DATETIME_FORMAT' }}">{{ campaign.ends_at|date:"M d, Y H:i" }}</time>
{% if campaign.ends_at < now %} {% if campaign.ends_at < now %}
@ -97,11 +77,15 @@
{% else %} {% else %}
(in {{ campaign.ends_at|timeuntil }}) (in {{ campaign.ends_at|timeuntil }})
{% endif %} {% endif %}
</p> </div>
{% endif %} {% endif %}
{% if campaign.starts_at %} {% if campaign.starts_at %}
<p style="margin: 0.25rem 0;"> <div>
<strong>Starts:</strong> {% if campaign.starts_at > now %}
Starts
{% else %}
Started at
{% endif %}
<time datetime="{{ campaign.starts_at|date:'c' }}" <time datetime="{{ campaign.starts_at|date:'c' }}"
title="{{ campaign.starts_at|date:'DATETIME_FORMAT' }}">{{ campaign.starts_at|date:"M d, Y H:i" }}</time> title="{{ campaign.starts_at|date:'DATETIME_FORMAT' }}">{{ campaign.starts_at|date:"M d, Y H:i" }}</time>
{% if campaign.starts_at < now %} {% if campaign.starts_at < now %}
@ -109,56 +93,37 @@
{% else %} {% else %}
(in {{ campaign.starts_at|timeuntil }}) (in {{ campaign.starts_at|timeuntil }})
{% endif %} {% endif %}
</p> </div>
{% endif %} {% endif %}
{% if campaign.duration %} {% if campaign.duration %}<div>Duration: {{ campaign.duration }}</div>{% endif %}
<p style="margin: 0.25rem 0;"> <div>
<strong>Duration:</strong> {{ campaign.duration }} Added <time datetime="{{ campaign.added_at|date:'c' }}"
</p> title="{{ campaign.added_at|date:'DATETIME_FORMAT' }}">{{ campaign.added_at|date:"M d, Y" }}</time>
{% endif %} ({{ campaign.added_at|timesince }} ago)
<p style="margin: 0.25rem 0;"> </div>
{% if reward_count %} <div>
{{ reward_count }} reward{{ reward_count|pluralize }} Updated <time datetime="{{ campaign.updated_at|date:'c' }}"
({{ total_watch_minutes }} total watch minute{{ total_watch_minutes|pluralize }}) title="{{ campaign.updated_at|date:'DATETIME_FORMAT' }}">{{ campaign.updated_at|date:"M d, Y" }}</time>
{% else %} ({{ campaign.updated_at|timesince }} ago)
No rewards </div>
{% endif %} <div>
</p>
<div style="margin-top: 0.5rem; font-size: 0.9rem;">
<strong>Participating channels:</strong>
{% if campaign.channels.all %} {% if campaign.channels.all %}
<ul style="margin: 0.25rem 0 0 0; padding-left: 1rem;"> Participating channels:
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.name }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.name }}</a>
{% endif %} {% endif %}
</li>
{% endfor %} {% endfor %}
{% if campaign.channels.count > 5 %} {% if campaign.channels.count > 5 %}and {{ campaign.channels.count|add:"-5" }} more{% endif %}
<li style="color: #666; font-style: italic;">... and {{ campaign.channels.count|add:"-5" }} more</li>
{% endif %}
</ul>
{% else %}
<p style="margin: 0.25rem 0 0 0;">
<!-- Is game wide-->
<a href="{{ campaign.category.kick_url }}"
rel="nofollow noopener"
target="_blank">{{ campaign.category.name }}</a> is game wide.
</p>
{% endif %} {% endif %}
</div> </div>
{% if campaign.created_at %} {% if campaign.created_at %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<time datetime="{{ campaign.created_at|date:'c' }}" <time datetime="{{ campaign.created_at|date:'c' }}"
title="{{ campaign.created_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.created_at|date:'DATETIME_FORMAT' }}">
Created: {{ campaign.created_at|date:"M d, Y H:i" }} ({{ campaign.created_at|timesince }} ago) Created {{ campaign.created_at|date:"M d, Y H:i" }} ({{ campaign.created_at|timesince }} ago)
</time> </time>
</p> </p>
{% endif %} {% endif %}
@ -166,19 +131,27 @@
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<time datetime="{{ campaign.api_updated_at|date:'c' }}" <time datetime="{{ campaign.api_updated_at|date:'c' }}"
title="{{ campaign.api_updated_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.api_updated_at|date:'DATETIME_FORMAT' }}">
API Updated: {{ campaign.api_updated_at|date:"M d, Y H:i" }} ({{ campaign.api_updated_at|timesince }} ago) API Updated {{ campaign.api_updated_at|date:"M d, Y H:i" }} ({{ campaign.api_updated_at|timesince }} ago)
</time> </time>
</p> </p>
{% endif %} {% endif %}
<p style="margin: 0.5rem 0 0 0;"> <div>
{% if campaign.url %}<a href="{{ campaign.url }}" rel="nofollow noopener" target="_blank">Details</a>{% endif %} <a href="{% url 'kick:game_campaign_feed' campaign.category.kick_id %}"
title="RSS feed for {{ campaign.category.name }} campaigns">[rss]</a>
<a href="{% url 'kick:game_campaign_feed_atom' campaign.category.kick_id %}"
title="Atom feed for {{ campaign.category.name }} campaigns">[atom]</a>
<a href="{% url 'kick:game_campaign_feed_discord' campaign.category.kick_id %}"
title="Discord feed for {{ campaign.category.name }} campaigns">[discord]</a>
<a href="{% url 'core:docs_rss' %}" title="RSS documentation">[explain]</a>
{% if campaign.connect_url %} {% if campaign.connect_url %}
{% if campaign.url %}-{% endif %}
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}"
rel="nofollow noopener" title="Connect URL for {{ campaign.name }}">[connect]</a>
target="_blank">Connect account</a>
{% endif %} {% endif %}
</p> {% if campaign.url %}<a href="{{ campaign.url }}" title="About URL for {{ campaign.name }}">[about]</a>{% endif %}
{% if campaign.channels.count == 0 %}
{% if campaign.rule_name == "Watch to redeem" %}<a href="{{ campaign.category.kick_url }}">[watch]</a>{% endif %}
{% endif %}
</div>
</div> </div>
</div> </div>
<hr /> <hr />
@ -210,9 +183,9 @@
<div> <div>
<h3 style="margin: 0 0 0.25rem 0;">{{ reward.name }}</h3> <h3 style="margin: 0 0 0.25rem 0;">{{ reward.name }}</h3>
{% if reward.required_units %} {% if reward.required_units %}
<p style="margin: 0.25rem 0;">{{ reward.required_units }} minutes watched</p> <p>Watch {{ reward.required_units }} minutes to earn</p>
{% else %} {% else %}
<p style="margin: 0.25rem 0;">No watch-time requirement</p> <p>No watch-time requirement</p>
{% endif %} {% endif %}
</div> </div>
</div> </div>

View file

@ -18,7 +18,13 @@
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<main> <main>
<h1>Kick Drop Campaigns</h1> <h1>List of Kick drops</h1>
<div>
<a href="{% url 'kick:dashboard' %}">Kick</a> > Campaigns
</div>
<div>
Scraped from <a href="https://web.kick.com/api/v1/drops/campaigns">Kick API</a>.
</div>
<div> <div>
<a href="{% url 'kick:campaign_feed' %}" <a href="{% url 'kick:campaign_feed' %}"
title="RSS feed for all campaigns">[rss]</a> title="RSS feed for all campaigns">[rss]</a>

View file

@ -60,9 +60,7 @@
</div> </div>
{% if category.kick_url %} {% if category.kick_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ category.kick_url }}" <a href="{{ category.kick_url }}">{{ category.kick_url }}</a>
rel="nofollow noopener"
target="_blank">{{ category.kick_url }}</a>
</p> </p>
{% endif %} {% endif %}
<p style="margin: 0.25rem 0; font-size: 0.85rem; color: #666;"> <p style="margin: 0.25rem 0; font-size: 0.85rem; color: #666;">
@ -161,9 +159,7 @@
{% endif %} {% endif %}
{% if campaign.connect_url %} {% if campaign.connect_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}">Connect account</a>
rel="nofollow noopener"
target="_blank">Connect account</a>
</p> </p>
{% endif %} {% endif %}
<div style="margin-top: 0.5rem; font-size: 0.9rem;"> <div style="margin-top: 0.5rem; font-size: 0.9rem;">
@ -173,13 +169,9 @@
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li> <li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.slug }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.slug }}</a>
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}
@ -190,9 +182,7 @@
{% else %} {% else %}
<p style="margin: 0.25rem 0 0 0;"> <p style="margin: 0.25rem 0 0 0;">
{% if category.kick_url %} {% if category.kick_url %}
<a href="{{ category.kick_url }}" <a href="{{ category.kick_url }}">{{ category.name }}</a> is game wide.
rel="nofollow noopener"
target="_blank">{{ category.name }}</a> is game wide.
{% else %} {% else %}
Game wide. Game wide.
{% endif %} {% endif %}
@ -304,9 +294,7 @@
{% endif %} {% endif %}
{% if campaign.connect_url %} {% if campaign.connect_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}">Connect account</a>
rel="nofollow noopener"
target="_blank">Connect account</a>
</p> </p>
{% endif %} {% endif %}
<div style="margin-top: 0.5rem; font-size: 0.9rem;"> <div style="margin-top: 0.5rem; font-size: 0.9rem;">
@ -316,13 +304,9 @@
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li> <li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.slug }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.slug }}</a>
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}
@ -333,9 +317,7 @@
{% else %} {% else %}
<p style="margin: 0.25rem 0 0 0;"> <p style="margin: 0.25rem 0 0 0;">
{% if category.kick_url %} {% if category.kick_url %}
<a href="{{ category.kick_url }}" <a href="{{ category.kick_url }}">{{ category.name }}</a> is game wide.
rel="nofollow noopener"
target="_blank">{{ category.name }}</a> is game wide.
{% else %} {% else %}
Game wide. Game wide.
{% endif %} {% endif %}
@ -447,9 +429,7 @@
{% endif %} {% endif %}
{% if campaign.connect_url %} {% if campaign.connect_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}">Connect account</a>
rel="nofollow noopener"
target="_blank">Connect account</a>
</p> </p>
{% endif %} {% endif %}
<div style="margin-top: 0.5rem; font-size: 0.9rem;"> <div style="margin-top: 0.5rem; font-size: 0.9rem;">
@ -459,13 +439,9 @@
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li> <li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.slug }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.slug }}</a>
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}
@ -476,9 +452,7 @@
{% else %} {% else %}
<p style="margin: 0.25rem 0 0 0;"> <p style="margin: 0.25rem 0 0 0;">
{% if category.kick_url %} {% if category.kick_url %}
<a href="{{ category.kick_url }}" <a href="{{ category.kick_url }}">{{ category.name }}</a> is game wide.
rel="nofollow noopener"
target="_blank">{{ category.name }}</a> is game wide.
{% else %} {% else %}
Game wide. Game wide.
{% endif %} {% endif %}

View file

@ -19,6 +19,9 @@
{% block content %} {% block content %}
<main> <main>
<h1>Kick Games</h1> <h1>Kick Games</h1>
<div>
<a href="{% url 'kick:dashboard' %}">Kick</a> > Games
</div>
<div> <div>
<a href="{% url 'kick:game_feed' %}" title="RSS feed for all games">[rss]</a> <a href="{% url 'kick:game_feed' %}" title="RSS feed for all games">[rss]</a>
<a href="{% url 'kick:game_feed_atom' %}" <a href="{% url 'kick:game_feed_atom' %}"

View file

@ -18,7 +18,10 @@
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<main> <main>
<h1>Active Kick Drop Campaigns</h1> <h1>Active Kick Drops</h1>
<div>
Scraped from <a href="https://web.kick.com/api/v1/drops/campaigns">Kick API</a>.
</div>
<!-- RSS Feeds --> <!-- RSS Feeds -->
<div> <div>
<a href="{% url 'kick:campaign_feed' %}" <a href="{% url 'kick:campaign_feed' %}"
@ -114,9 +117,7 @@
{% endif %} {% endif %}
{% if campaign.connect_url %} {% if campaign.connect_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}">Connect account</a>
rel="nofollow noopener"
target="_blank">Connect account</a>
</p> </p>
{% endif %} {% endif %}
<div style="margin-top: 0.5rem; font-size: 0.9rem;"> <div style="margin-top: 0.5rem; font-size: 0.9rem;">
@ -126,13 +127,9 @@
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li> <li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.slug }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.slug }}</a>
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}

View file

@ -19,6 +19,9 @@
{% block content %} {% block content %}
<main> <main>
<h1>Kick Organizations</h1> <h1>Kick Organizations</h1>
<div>
<a href="{% url 'kick:dashboard' %}">Kick</a> > Organizations
</div>
<div> <div>
<a href="{% url 'kick:organization_feed' %}" <a href="{% url 'kick:organization_feed' %}"
title="RSS feed for all organizations">[rss]</a> title="RSS feed for all organizations">[rss]</a>
@ -34,7 +37,7 @@
<li> <li>
<a href="{% url 'kick:organization_detail' org.kick_id %}">{{ org.name }}</a> <a href="{% url 'kick:organization_detail' org.kick_id %}">{{ org.name }}</a>
{% if org.campaign_count %}- {{ org.campaign_count }} campaign{{ org.campaign_count|pluralize }}{% endif %} {% if org.campaign_count %}- {{ org.campaign_count }} campaign{{ org.campaign_count|pluralize }}{% endif %}
{% if org.url %}- <a href="{{ org.url }}" rel="nofollow noopener" target="_blank">{{ org.url }}</a>{% endif %} {% if org.url %}- <a href="{{ org.url }}">{{ org.url }}</a>{% endif %}
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>

View file

@ -28,7 +28,7 @@
{% if org.restricted %}<p style="margin: 0.25rem 0; color: #b00;">Restricted</p>{% endif %} {% if org.restricted %}<p style="margin: 0.25rem 0; color: #b00;">Restricted</p>{% endif %}
{% if org.url %} {% if org.url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ org.url }}" rel="nofollow noopener" target="_blank">{{ org.url }}</a> <a href="{{ org.url }}">{{ org.url }}</a>
</p> </p>
{% endif %} {% endif %}
<p style="margin: 0.25rem 0; font-size: 0.85rem; color: #666;"> <p style="margin: 0.25rem 0; font-size: 0.85rem; color: #666;">
@ -121,9 +121,7 @@
{% endif %} {% endif %}
{% if campaign.connect_url %} {% if campaign.connect_url %}
<p style="margin: 0.25rem 0;"> <p style="margin: 0.25rem 0;">
<a href="{{ campaign.connect_url }}" <a href="{{ campaign.connect_url }}">Connect account</a>
rel="nofollow noopener"
target="_blank">Connect account</a>
</p> </p>
{% endif %} {% endif %}
<div style="margin-top: 0.5rem; font-size: 0.9rem;"> <div style="margin-top: 0.5rem; font-size: 0.9rem;">
@ -133,13 +131,9 @@
{% for channel in campaign.channels.all|slice:":5" %} {% for channel in campaign.channels.all|slice:":5" %}
<li> <li>
{% if channel.user %} {% if channel.user %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.user.username }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.user.username }}</a>
{% else %} {% else %}
<a href="{{ channel.channel_url }}" <a href="{{ channel.channel_url }}">{{ channel.slug }}</a>
rel="nofollow noopener"
target="_blank">{{ channel.slug }}</a>
{% endif %} {% endif %}
</li> </li>
{% endfor %} {% endfor %}

View file

@ -5,6 +5,9 @@
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<h1>{{ badge_sets.count }} Twitch Chat Badges</h1> <h1>{{ badge_sets.count }} Twitch Chat Badges</h1>
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Badges
</div>
{% if badge_sets %} {% if badge_sets %}
{% for data in badge_data %} {% for data in badge_data %}
<h2> <h2>

View file

@ -5,6 +5,9 @@
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<h1>{{ badge_set.set_id }}</h1> <h1>{{ badge_set.set_id }}</h1>
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > <a href="{% url 'twitch:badge_list' %}">Badges</a> > {{ badge_set.set_id }}
</div>
{% if badges %} {% if badges %}
<table> <table>
<tbody> <tbody>
@ -19,12 +22,12 @@
<td>{{ badge.title }}</td> <td>{{ badge.title }}</td>
<td>{{ badge.description }}</td> <td>{{ badge.description }}</td>
<td> <td>
<a href="{{ badge.image_url_1x }}" rel="nofollow ugc">[18px]</a> <a href="{{ badge.image_url_1x }}">[18px]</a>
<a href="{{ badge.image_url_2x }}" rel="nofollow ugc">[36px]</a> <a href="{{ badge.image_url_2x }}">[36px]</a>
<a href="{{ badge.image_url_4x }}" rel="nofollow ugc">[72px]</a> <a href="{{ badge.image_url_4x }}">[72px]</a>
</td> </td>
<td> <td>
{% if badge.click_url %}<a href="{{ badge.click_url }}" rel="nofollow ugc">{{ badge.click_action }}</a>{% endif %} {% if badge.click_url %}<a href="{{ badge.click_url }}">{{ badge.click_action }}</a>{% endif %}
</td> </td>
</tr> </tr>
{% if badge.award_campaigns %} {% if badge.award_campaigns %}

View file

@ -31,71 +31,70 @@
<!-- Campaign Title --> <!-- Campaign Title -->
<div style="display: flex; flex-direction: column;"> <div style="display: flex; flex-direction: column;">
<h1 style="margin-top: 0; margin-bottom: 0px;">{{ campaign.clean_name }}</h1> <h1 style="margin-top: 0; margin-bottom: 0px;">{{ campaign.clean_name }}</h1>
<!-- Add breadcrumbs -->
<div> <div>
{% if campaign.game %} <a href="{% url 'twitch:dashboard' %}">Twitch</a> > <a href="{% url 'twitch:campaign_list' %}">Campaigns</a> > <a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.display_name }}</a> > {{ campaign.clean_name }}
<a href="{% url 'twitch:game_detail' campaign.game.twitch_id %}">{{ campaign.game.get_game_name }}</a>
{% endif %}
{% if owners %}
-
<!-- Campaign Owners -->
{% for org in owners %}
<a href="{% url 'twitch:organization_detail' org.twitch_id %}">{{ org.name }}</a>
{% endfor %}
{% endif %}
</div> </div>
<!-- Game owner -->
{% if owners %}
<div style="font-size: 0.9em;">
Owned by
{% for owner in owners %}
<a href="{% url 'twitch:organization_detail' owner.twitch_id %}">{{ owner.name }}</a>
{% if not forloop.last %}-{% endif %}
{% endfor %}
</div>
{% endif %}
<!-- Campaign description --> <!-- Campaign description -->
<p>{{ campaign.description|linebreaksbr }}</p> <p>{{ campaign.description|linebreaksbr }}</p>
<small> <div>
Published: Published
<time datetime="{{ campaign.added_at|date:'c' }}" <time datetime="{{ campaign.added_at|date:'c' }}"
title="{{ campaign.added_at|date:'DATETIME_FORMAT' }}">{{ campaign.added_at|date:"M d, Y H:i" }}</time> title="{{ campaign.added_at|date:'DATETIME_FORMAT' }}">{{ campaign.added_at|date:"M d, Y H:i" }}</time> ({{ campaign.added_at|timesince }} ago)
· Last updated: </div>
<div>
Last updated
<time datetime="{{ campaign.updated_at|date:'c' }}" <time datetime="{{ campaign.updated_at|date:'c' }}"
title="{{ campaign.updated_at|date:'DATETIME_FORMAT' }}">{{ campaign.updated_at|date:"M d, Y H:i" }}</time> title="{{ campaign.updated_at|date:'DATETIME_FORMAT' }}">{{ campaign.updated_at|date:"M d, Y H:i" }}</time> ({{ campaign.updated_at|timesince }} ago)
</small> </div>
<!-- Campaign end times --> <!-- Campaign end times -->
<div> <div>
{% if campaign.end_at < now %} {% if campaign.end_at < now %}
Ended
<time datetime="{{ campaign.end_at|date:'c' }}" <time datetime="{{ campaign.end_at|date:'c' }}"
title="{{ campaign.end_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.end_at|date:'DATETIME_FORMAT' }}">{{ campaign.end_at|date:"M d, Y H:i" }}</time> ({{ campaign.end_at|timesince }} ago)
<strong>Ended</strong> {{ campaign.end_at|timesince }} ago
</time>
{% else %} {% else %}
Ends in
<time datetime="{{ campaign.end_at|date:'c' }}" <time datetime="{{ campaign.end_at|date:'c' }}"
title="{{ campaign.end_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.end_at|date:'DATETIME_FORMAT' }}">{{ campaign.end_at|date:"M d, Y H:i" }}</time> (in {{ campaign.end_at|timeuntil }})
<strong>Ends in</strong> {{ campaign.end_at|timeuntil }}
</time>
{% endif %} {% endif %}
</div> </div>
<!-- Campaign start times --> <!-- Campaign start times -->
<div> <div>
{% if campaign.start_at > now %} {% if campaign.start_at > now %}
Starts in
<time datetime="{{ campaign.start_at|date:'c' }}" <time datetime="{{ campaign.start_at|date:'c' }}"
title="{{ campaign.start_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.start_at|date:'DATETIME_FORMAT' }}">{{ campaign.start_at|date:"M d, Y H:i" }}</time> (in {{ campaign.start_at|timeuntil }})
<strong>Starts in</strong> {{ campaign.start_at|timeuntil }}
</time>
{% else %} {% else %}
Started
<time datetime="{{ campaign.start_at|date:'c' }}" <time datetime="{{ campaign.start_at|date:'c' }}"
title="{{ campaign.start_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.start_at|date:'DATETIME_FORMAT' }}">{{ campaign.start_at|date:"M d, Y H:i" }}</time> ({{ campaign.start_at|timesince }} ago)
<strong>Started</strong> {{ campaign.start_at|timesince }} ago
</time>
{% endif %} {% endif %}
</div> </div>
<!-- Campaign duration --> <!-- Campaign duration -->
<div> <div>
Duration is
<time datetime="{{ campaign.duration_iso }}" <time datetime="{{ campaign.duration_iso }}"
title="{{ campaign.start_at|date:'DATETIME_FORMAT' }} to {{ campaign.end_at|date:'DATETIME_FORMAT' }}"> title="{{ campaign.start_at|date:'DATETIME_FORMAT' }} to {{ campaign.end_at|date:'DATETIME_FORMAT' }}">
<strong>Duration</strong> {{ campaign.end_at|timeuntil:campaign.start_at }} {{ campaign.end_at|timeuntil:campaign.start_at }}
</time> </time>
</div> </div>
<!-- Buttons --> <!-- Buttons -->
<div> <div>
<!-- Campaign Detail URL --> <!-- Campaign Detail URL -->
{% if campaign.details_url %}<a href="{{ campaign.details_url }}" rel="nofollow ugc">[details]</a>{% endif %} {% if campaign.details_url %}<a href="{{ campaign.details_url }}">[details]</a>{% endif %}
<!-- Campaign Account Link URL --> <!-- Campaign Account Link URL -->
{% if campaign.account_link_url %} {% if campaign.account_link_url %}<a href="{{ campaign.account_link_url }}">[connect]</a>{% endif %}
<a href="{{ campaign.account_link_url }}" rel="nofollow ugc">[connect]</a>
{% endif %}
<!-- RSS Feeds --> <!-- RSS Feeds -->
{% if campaign.game %} {% if campaign.game %}
<a href="{% url 'core:game_campaign_feed' campaign.game.twitch_id %}" <a href="{% url 'core:game_campaign_feed' campaign.game.twitch_id %}"
@ -183,7 +182,6 @@
{% else %} {% else %}
<!-- If no allowed channels are specified, link to the game's Twitch directory for channels with drops enabled --> <!-- If no allowed channels are specified, link to the game's Twitch directory for channels with drops enabled -->
<a href="{{ campaign.game.twitch_directory_url }}" <a href="{{ campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
title="Find streamers playing {{ campaign.game.display_name }} with drops enabled"> title="Find streamers playing {{ campaign.game.display_name }} with drops enabled">
Go to a participating live channel Go to a participating live channel
</a> </a>

View file

@ -21,7 +21,10 @@
{% block content %} {% block content %}
<main> <main>
<header> <header>
<h1>Drop Campaigns</h1> <h1>List of Twitch Drop Campaigns</h1>
<div>
<a href="{% url 'core:dashboard' %}">Dashboard</a> > Drops
</div>
<!-- RSS Feeds --> <!-- RSS Feeds -->
<div> <div>
<a href="{% url 'core:campaign_feed' %}" <a href="{% url 'core:campaign_feed' %}"

View file

@ -5,29 +5,37 @@
{% block content %} {% block content %}
<!-- Channel Title --> <!-- Channel Title -->
<h1>{{ channel.display_name }}</h1> <h1>{{ channel.display_name }}</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > <a href="{% url 'twitch:channel_list' %}">Channels</a> > {{ channel.display_name }}
</div>
{% if channel.display_name != channel.name %} {% if channel.display_name != channel.name %}
<p> <div>
Username: <code>{{ channel.name }}</code> Username: <code>{{ channel.name }}</code>
</p> </div>
{% endif %} {% endif %}
<!-- Channel Info -->
<div>Channel ID: {{ channel.twitch_id }}</div>
<div>
Published
<time datetime="{{ channel.added_at|date:'c' }}"
title="{{ channel.added_at|date:'DATETIME_FORMAT' }}">{{ channel.added_at|date:"M d, Y H:i" }}</time> ({{ channel.added_at|timesince }} ago)
</div>
<div>
Last updated
<time datetime="{{ channel.updated_at|date:'c' }}"
title="{{ channel.updated_at|date:'DATETIME_FORMAT' }}">{{ channel.updated_at|date:"M d, Y H:i" }}</time> ({{ channel.updated_at|timesince }} ago)
</div>
<!-- Twitch Stream Embed --> <!-- Twitch Stream Embed -->
<iframe src="https://player.twitch.tv/?channel={{ channel.name }}&parent={{ request.get_host }}&muted=false" <iframe src="https://player.twitch.tv/?channel={{ channel.name }}&parent={{ request.get_host }}&muted=false"
height="480" height="480"
width="100%" width="100%"
allowfullscreen allowfullscreen
style="max-width: 854px; style="max-width: 854px;
border: none"> border: none;
margin-top: 16px;
margin-bottom: 16px">
</iframe> </iframe>
<!-- Channel Info -->
<p>Channel ID: {{ channel.twitch_id }}</p>
<p>
Published:
<time datetime="{{ channel.added_at|date:'c' }}"
title="{{ channel.added_at|date:'DATETIME_FORMAT' }}">{{ channel.added_at|date:"M d, Y H:i" }}</time>
· Last updated:
<time datetime="{{ channel.updated_at|date:'c' }}"
title="{{ channel.updated_at|date:'DATETIME_FORMAT' }}">{{ channel.updated_at|date:"M d, Y H:i" }}</time>
</p>
{% if active_campaigns %} {% if active_campaigns %}
<h5>Active Campaigns</h5> <h5>Active Campaigns</h5>
<table> <table>

View file

@ -5,6 +5,9 @@
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<h1>Channels</h1> <h1>Channels</h1>
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Channels
</div>
<p>Browse all channels that can participate in drop campaigns</p> <p>Browse all channels that can participate in drop campaigns</p>
<form method="get" action="{% url 'twitch:channel_list' %}"> <form method="get" action="{% url 'twitch:channel_list' %}">
<input type="text" <input type="text"

View file

@ -20,11 +20,8 @@
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<main> <main>
<h1>Active Twitch Drops Campaigns</h1> <h1>List of all active Twitch drops.</h1>
<p> <p>
This page lists all currently active Twitch Drops campaigns
organized by game.
<br />
Click on a campaign for more details about it and how to Click on a campaign for more details about it and how to
earn drops. earn drops.
<br /> <br />
@ -108,7 +105,6 @@
{% for channel in campaign_data.allowed_channels|slice:":5" %} {% for channel in campaign_data.allowed_channels|slice:":5" %}
<li style="margin-bottom: 0.1rem;"> <li style="margin-bottom: 0.1rem;">
<a href="https://twitch.tv/{{ channel.name }}" <a href="https://twitch.tv/{{ channel.name }}"
rel="nofollow ugc"
title="Watch {{ channel.display_name }} on Twitch"> title="Watch {{ channel.display_name }} on Twitch">
{{ channel.display_name }}</a><a href="{% url 'twitch:channel_detail' channel.twitch_id %}" {{ channel.display_name }}</a><a href="{% url 'twitch:channel_detail' channel.twitch_id %}"
title="View {{ channel.display_name }} details" title="View {{ channel.display_name }} details"
@ -121,7 +117,6 @@
{% if campaign.game.twitch_directory_url %} {% if campaign.game.twitch_directory_url %}
<li> <li>
<a href="{{ campaign.game.twitch_directory_url }}" <a href="{{ campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
title="Open Twitch category page for {{ campaign_data.campaign.game.display_name }} with Drops filter"> title="Open Twitch category page for {{ campaign_data.campaign.game.display_name }} with Drops filter">
Browse {{ campaign_data.campaign.game.display_name }} category Browse {{ campaign_data.campaign.game.display_name }} category
</a> </a>
@ -139,7 +134,6 @@
{% if campaign_data.campaign.game.twitch_directory_url %} {% if campaign_data.campaign.game.twitch_directory_url %}
<li> <li>
<a href="{{ campaign_data.campaign.game.twitch_directory_url }}" <a href="{{ campaign_data.campaign.game.twitch_directory_url }}"
rel="nofollow ugc"
title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled"> title="Find streamers playing {{ campaign_data.campaign.game.display_name }} with drops enabled">
Go to a participating live channel Go to a participating live channel
</a> </a>
@ -236,7 +230,6 @@
{% if campaign.external_url %} {% if campaign.external_url %}
<div style="margin-top: 0.75rem;"> <div style="margin-top: 0.75rem;">
<a href="{{ campaign.external_url }}" <a href="{{ campaign.external_url }}"
rel="nofollow ugc"
style="display: inline-block; style="display: inline-block;
padding: 0.5rem 1rem; padding: 0.5rem 1rem;
background-color: #9146ff; background-color: #9146ff;

View file

@ -5,6 +5,10 @@
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<h1>Emotes</h1> <h1>Emotes</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Emotes
</div>
{% for emote in emotes %} {% for emote in emotes %}
<a href="{% url 'twitch:campaign_detail' emote.campaign.twitch_id %}" <a href="{% url 'twitch:campaign_detail' emote.campaign.twitch_id %}"
title="{{ emote.campaign.name }}" title="{{ emote.campaign.name }}"

View file

@ -33,28 +33,30 @@
{{ game.display_name }} {{ game.display_name }}
{% if game.display_name != game.name and game.name %}<small>({{ game.name }})</small>{% endif %} {% if game.display_name != game.name and game.name %}<small>({{ game.name }})</small>{% endif %}
</h1> </h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > <a href="{% url 'twitch:games_grid' %}">Games</a> > {{ game.display_name }}
</div>
<!-- Game owner --> <!-- Game owner -->
{% if owners %} {% if owners %}
<small> <div style="font-size: 0.9em;">
Owned by Owned by
{% for owner in owners %} {% for owner in owners %}
<a href="{% url 'twitch:organization_detail' owner.twitch_id %}">{{ owner.name }}</a> <a href="{% url 'twitch:organization_detail' owner.twitch_id %}">{{ owner.name }}</a>
{% if not forloop.last %},{% endif %} {% if not forloop.last %}-{% endif %}
{% endfor %} {% endfor %}
</small> </div>
{% endif %} {% endif %}
<div> <div>
Twitch ID: <a href="https://www.twitch.tv/directory/category/{{ game.slug|urlencode }}">{{ game.twitch_id }}</a> Published
</div>
<div>Twitch slug: {{ game.slug }}</div>
<small>
Published:
<time datetime="{{ game.added_at|date:'c' }}" <time datetime="{{ game.added_at|date:'c' }}"
title="{{ game.added_at|date:'DATETIME_FORMAT' }}">{{ game.added_at|date:"M d, Y H:i" }}</time> title="{{ game.added_at|date:'DATETIME_FORMAT' }}">{{ game.added_at|date:"M d, Y H:i" }}</time> ({{ game.added_at|timesince }} ago)
· Last updated: </div>
<div>
Last updated
<time datetime="{{ game.updated_at|date:'c' }}" <time datetime="{{ game.updated_at|date:'c' }}"
title="{{ game.updated_at|date:'DATETIME_FORMAT' }}">{{ game.updated_at|date:"M d, Y H:i" }}</time> title="{{ game.updated_at|date:'DATETIME_FORMAT' }}">{{ game.updated_at|date:"M d, Y H:i" }}</time> ({{ game.updated_at|timesince }} ago)
</small> </div>
<!-- RSS Feeds --> <!-- RSS Feeds -->
<div> <div>
<a href="{% url 'core:game_campaign_feed' game.twitch_id %}" <a href="{% url 'core:game_campaign_feed' game.twitch_id %}"

View file

@ -20,7 +20,11 @@
{% block content %} {% block content %}
<main> <main>
<header> <header>
<h1 id="page-title">All Games</h1> <h1>All Games</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Games
</div>
<div> <div>
<a href="{% url 'twitch:games_list' %}" title="View games as list">[list]</a> <a href="{% url 'twitch:games_list' %}" title="View games as list">[list]</a>
<a href="{% url 'core:game_feed' %}" title="RSS feed for all games">[rss]</a> <a href="{% url 'core:game_feed' %}" title="RSS feed for all games">[rss]</a>
@ -40,8 +44,7 @@
<div style="display: flex; flex-wrap: wrap; gap: 0.25rem;"> <div style="display: flex; flex-wrap: wrap; gap: 0.25rem;">
{% for organization, games in games_by_org.items %} {% for organization, games in games_by_org.items %}
{% for item in games %} {% for item in games %}
<article id="game-{{ item.game.twitch_id }}" <article style="padding: 0.25rem;
style="padding: 0.25rem;
border-radius: 8px; border-radius: 8px;
flex: 1 1 160px; flex: 1 1 160px;
text-align: center"> text-align: center">

View file

@ -19,6 +19,10 @@
{% block content %} {% block content %}
<main> <main>
<h1>Games List</h1> <h1>Games List</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Games
</div>
<div> <div>
<a href="{% url 'twitch:games_grid' %}" title="View games as grid">[grid]</a> <a href="{% url 'twitch:games_grid' %}" title="View games as grid">[grid]</a>
<a href="{% url 'core:game_feed' %}" title="RSS feed for all games">[rss]</a> <a href="{% url 'core:game_feed' %}" title="RSS feed for all games">[rss]</a>

View file

@ -4,6 +4,10 @@
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<h1>Organizations</h1> <h1>Organizations</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Organizations
</div>
<div> <div>
<a href="{% url 'core:organization_feed' %}" <a href="{% url 'core:organization_feed' %}"
title="RSS feed for all organizations">[rss]</a> title="RSS feed for all organizations">[rss]</a>

View file

@ -22,14 +22,20 @@
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<h1>{{ organization.name }}</h1> <h1>{{ organization.name }}</h1>
<p> <!-- Add breadcrumbs -->
Published: <div>
<a href="{% url 'core:dashboard' %}">Dashboard</a> > <a href="{% url 'twitch:org_list' %}">Organizations</a> > {{ organization.name }}
</div>
<div>
Published
<time datetime="{{ organization.added_at|date:'c' }}" <time datetime="{{ organization.added_at|date:'c' }}"
title="{{ organization.added_at|date:'DATETIME_FORMAT' }}">{{ organization.added_at|date:"M d, Y H:i" }}</time> title="{{ organization.added_at|date:'DATETIME_FORMAT' }}">{{ organization.added_at|date:"M d, Y H:i" }}</time> ({{ organization.added_at|timesince }} ago)
· Last updated: </div>
<div>
Last updated
<time datetime="{{ organization.updated_at|date:'c' }}" <time datetime="{{ organization.updated_at|date:'c' }}"
title="{{ organization.updated_at|date:'DATETIME_FORMAT' }}">{{ organization.updated_at|date:"M d, Y H:i" }}</time> title="{{ organization.updated_at|date:'DATETIME_FORMAT' }}">{{ organization.updated_at|date:"M d, Y H:i" }}</time> ({{ organization.updated_at|timesince }} ago)
</p> </div>
<header> <header>
<h2>Games by {{ organization.name }}</h2> <h2>Games by {{ organization.name }}</h2>
</header> </header>

View file

@ -19,42 +19,40 @@
href="{% url 'core:reward_campaign_feed_discord' %}" /> href="{% url 'core:reward_campaign_feed_discord' %}" />
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<div style="display: flex; align-items: flex-start;">
<!-- Campaign image -->
<div style="margin-right: 16px;">
{% if reward_campaign.image_best_url %}
{% picture reward_campaign.image_best_url alt=reward_campaign.name width=160 %}
{% endif %}
</div>
<div style="display: flex; flex-direction: column;">
<!-- Campaign Title --> <!-- Campaign Title -->
{% if reward_campaign.brand %} {% if reward_campaign.brand %}
<h1 id="campaign-title">{{ reward_campaign.brand }}: {{ reward_campaign.name }}</h1> <h1 id="campaign-title">{{ reward_campaign.brand }}: {{ reward_campaign.name }}</h1>
{% else %} {% else %}
<h1 id="campaign-title">{{ reward_campaign.name }}</h1> <h1 id="campaign-title">{{ reward_campaign.name }}</h1>
{% endif %} {% endif %}
<!-- Back to list link --> <!-- Game eligibility -->
<p> <div>
<a href="{% url 'twitch:reward_campaign_list' %}">← Back to Reward Campaigns</a> Game eligibility:
</p> {% if reward_campaign.game %}
<!-- Campaign image --> <a href="{% url 'twitch:game_detail' reward_campaign.game.twitch_id %}">{{ reward_campaign.game.display_name }}</a>
{% if reward_campaign.image_best_url %} {% elif reward_campaign.is_sitewide %}
{% picture reward_campaign.image_best_url alt=reward_campaign.name width=160 %} Site-wide
{% else %}
None
{% endif %} {% endif %}
<!-- RSS Feeds --> </div>
<div style="margin-bottom: 1rem;"> <!-- Add breadcrumbs -->
<a href="{% url 'core:reward_campaign_feed' %}" <div>
style="margin-right: 1rem" <a href="{% url 'twitch:dashboard' %}">Twitch</a> > <a href="{% url 'twitch:reward_campaign_list' %}">Reward Campaigns</a> > {{ reward_campaign.name }}
title="RSS feed for all reward campaigns">[rss]</a>
<a href="{% url 'core:reward_campaign_feed_atom' %}"
title="Atom feed for all reward campaigns">[atom]</a>
<a href="{% url 'core:reward_campaign_feed_discord' %}"
title="Discord feed for all reward campaigns">[discord]</a>
<a href="{% url 'core:docs_rss' %}" title="RSS feed documentation">[explain]</a>
</div> </div>
<!-- Campaign Summary --> <!-- Campaign Summary -->
{% if reward_campaign.summary %}<p id="campaign-summary">{{ reward_campaign.summary|linebreaksbr }}</p>{% endif %} {% if reward_campaign.summary %}<p id="campaign-summary">{{ reward_campaign.summary|linebreaksbr }}</p>{% endif %}
<!-- Campaign Status --> <!-- Campaign Status -->
<h5>Status</h5> <div>
<table>
<tbody>
<tr>
<td>
<strong>Status:</strong> <strong>Status:</strong>
</td>
<td>
{% if is_active %} {% if is_active %}
Active Active
{% elif reward_campaign.starts_at > now %} {% elif reward_campaign.starts_at > now %}
@ -62,107 +60,66 @@
{% else %} {% else %}
Expired Expired
{% endif %} {% endif %}
</td> </div>
</tr> <div>Added at {{ reward_campaign.added_at|date:"M d, Y H:i" }} ({{ reward_campaign.added_at|timesince }} ago)</div>
<tr> <div>
<td> Last updated at {{ reward_campaign.updated_at|date:"M d, Y H:i" }} ({{ reward_campaign.updated_at|timesince }} ago)
<strong>Starts:</strong> </div>
</td> <!-- Reward Start Time -->
<td> <div>
{% if reward_campaign.starts_at > now %}
Starts
{% else %}
Started at
{% endif %}
<time datetime="{{ reward_campaign.starts_at|date:'c' }}" <time datetime="{{ reward_campaign.starts_at|date:'c' }}"
title="{{ reward_campaign.starts_at|date:'DATETIME_FORMAT' }}"> title="{{ reward_campaign.starts_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.starts_at|date:"M d, Y H:i" }} {{ reward_campaign.starts_at|date:"M d, Y H:i" }}
{% if reward_campaign.starts_at > now %}
(in {{ reward_campaign.starts_at|timeuntil }})
{% else %}
({{ reward_campaign.starts_at|timesince }} ago)
{% endif %}
</time> </time>
</td> </div>
</tr> <!-- Reward End Time -->
<tr> <div>
<td> {% if reward_campaign.ends_at > now %}
<strong>Ends:</strong> Ends
</td> {% else %}
<td> Ended at
{% endif %}
<time datetime="{{ reward_campaign.ends_at|date:'c' }}" <time datetime="{{ reward_campaign.ends_at|date:'c' }}"
title="{{ reward_campaign.ends_at|date:'DATETIME_FORMAT' }}"> title="{{ reward_campaign.ends_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.ends_at|date:"M d, Y H:i" }} {{ reward_campaign.ends_at|date:"M d, Y H:i" }}
{% if reward_campaign.ends_at > now %}
(in {{ reward_campaign.ends_at|timeuntil }})
{% else %}
({{ reward_campaign.ends_at|timesince }} ago)
{% endif %}
</time> </time>
</td> </div>
</tr> <div>
{% if reward_campaign.game %} {% if reward_campaign.instructions %}{{ reward_campaign.instructions|linebreaksbr }}{% endif %}
<tr> </div>
<td> <!-- RSS Feeds -->
<strong>Game:</strong> <div style="margin-bottom: 1rem;">
</td> <a href="{% url 'core:reward_campaign_feed' %}"
<td> title="RSS feed for all reward campaigns">[rss]</a>
<a href="{% url 'twitch:game_detail' reward_campaign.game.twitch_id %}">{{ reward_campaign.game.display_name }}</a> <a href="{% url 'core:reward_campaign_feed_atom' %}"
</td> title="Atom feed for all reward campaigns">[atom]</a>
</tr> <a href="{% url 'core:reward_campaign_feed_discord' %}"
{% elif reward_campaign.is_sitewide %} title="Discord feed for all reward campaigns">[discord]</a>
<tr> <a href="{% url 'core:docs_rss' %}" title="RSS feed documentation">[explain]</a>
<td>
<strong>Type:</strong>
</td>
<td>Site-wide reward campaign</td>
</tr>
{% endif %}
</tbody>
</table>
<!-- Instructions -->
{% if reward_campaign.instructions %}
<h5>Instructions</h5>
<p>{{ reward_campaign.instructions|linebreaksbr }}</p>
{% endif %}
<!-- Actions -->
{% if reward_campaign.external_url or reward_campaign.about_url %}
<p>
{% if reward_campaign.external_url %} {% if reward_campaign.external_url %}
<a href="{{ reward_campaign.external_url }}" rel="nofollow ugc">Claim Reward →</a> <a href="{{ reward_campaign.external_url }}"
title="External URL for {{ reward_campaign.name }}">[external]</a>
{% endif %} {% endif %}
{% if reward_campaign.about_url %} {% if reward_campaign.about_url %}
<a href="{{ reward_campaign.about_url }}" rel="nofollow ugc">Learn More →</a> <a href="{{ reward_campaign.about_url }}"
title="About URL for {{ reward_campaign.name }}">[about]</a>
{% endif %} {% endif %}
</p> </div>
{% endif %} </div>
<!-- Metadata --> </div>
<h5>Campaign Information</h5>
<table>
<tbody>
{% if reward_campaign.brand %}
<tr>
<td>
<strong>Brand:</strong>
</td>
<td>{{ reward_campaign.brand }}</td>
</tr>
{% endif %}
<tr>
<td>
<strong>Twitch ID:</strong>
</td>
<td>
<code>{{ reward_campaign.twitch_id }}</code>
</td>
</tr>
<tr>
<td>
<strong>Added to tracker:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.added_at|date:'c' }}"
title="{{ reward_campaign.added_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.added_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
<tr>
<td>
<strong>Last updated:</strong>
</td>
<td>
<time datetime="{{ reward_campaign.updated_at|date:'c' }}"
title="{{ reward_campaign.updated_at|date:'DATETIME_FORMAT' }}">
{{ reward_campaign.updated_at|date:"M d, Y H:i" }}
</time>
</td>
</tr>
</tbody>
</table>
{% endblock content %} {% endblock content %}

View file

@ -18,7 +18,16 @@
href="{% url 'core:reward_campaign_feed_discord' %}" /> href="{% url 'core:reward_campaign_feed_discord' %}" />
{% endblock extra_head %} {% endblock extra_head %}
{% block content %} {% block content %}
<h1>Reward Campaigns</h1> <h1>List of Twitch Reward Campaigns</h1>
<!-- Add breadcrumbs -->
<div>
<a href="{% url 'twitch:dashboard' %}">Twitch</a> > Reward Campaigns
</div>
<p>This is an archive of old Twitch reward campaigns because we currently do not scrape them.</p>
<p>
Feel free to submit a pull request on <a href="https://github.com/TheLovinator1/ttvdrops">GitHub</a>
with a working implementation :-).
</p>
<!-- RSS Feeds --> <!-- RSS Feeds -->
<div> <div>
<a href="{% url 'core:reward_campaign_feed' %}" <a href="{% url 'core:reward_campaign_feed' %}"
@ -29,11 +38,6 @@
title="Discord feed for all reward campaigns">[discord]</a> title="Discord feed for all reward campaigns">[discord]</a>
<a href="{% url 'core:docs_rss' %}" title="RSS feed documentation">[explain]</a> <a href="{% url 'core:docs_rss' %}" title="RSS feed documentation">[explain]</a>
</div> </div>
<p>This is an archive of old Twitch reward campaigns because we do not monitor them.</p>
<p>
Feel free to submit a pull request on <a href="https://github.com/TheLovinator1/ttvdrops">GitHub</a>
with a working implementation :-).
</p>
{% if reward_campaigns %} {% if reward_campaigns %}
{% comment %} {% comment %}
<h5>Active Reward Campaigns</h5> <h5>Active Reward Campaigns</h5>

View file

@ -6,9 +6,7 @@
<main> <main>
<h1>YouTube channels with rewards.</h1> <h1>YouTube channels with rewards.</h1>
<p> <p>
<a href="https://support.google.com/youtube/answer/9024948?hl=en&co=GENIE.Platform%3DAndroid" <a href="https://support.google.com/youtube/answer/9024948?hl=en&co=GENIE.Platform%3DAndroid">YouTube Help Center</a> has the following channels listed where you can earn rewards:
rel="noopener"
target="_blank">YouTube Help Center</a> has the following channels listed where you can earn rewards:
</p> </p>
<table> <table>
<thead> <thead>
@ -22,7 +20,7 @@
{% for item in group.channels %} {% for item in group.channels %}
<tr> <tr>
<td> <td>
<a href="{{ item.url }}" rel="noopener" target="_blank">{{ item.channel }}</a> <a href="{{ item.url }}">{{ item.channel }}</a>
</td> </td>
<td>{{ group.organization }}</td> <td>{{ group.organization }}</td>
</tr> </tr>

View file

@ -918,11 +918,11 @@ class GameFeed(TTVDropsBaseFeed):
# MARK: /rss/campaigns/ # MARK: /rss/campaigns/
class DropCampaignFeed(TTVDropsBaseFeed): class DropCampaignFeed(TTVDropsBaseFeed):
"""RSS feed for latest drop campaigns.""" """RSS feed for latest Twitch drops."""
title: str = "Twitch Drop Campaigns" title: str = "Twitch Drops"
link: str = "/" link: str = "/"
description: str = "Latest Twitch drop campaigns on TTVDrops" description: str = "Latest Twitch drops"
item_guid_is_permalink = True item_guid_is_permalink = True
_limit: int | None = None _limit: int | None = None

View file

@ -457,7 +457,7 @@ def drop_campaign_list_view(request: HttpRequest) -> HttpResponse: # noqa: PLR0
except EmptyPage: except EmptyPage:
campaigns = paginator.page(paginator.num_pages) campaigns = paginator.page(paginator.num_pages)
title = "Twitch Drop Campaigns" title = "Twitch Drops"
if status_filter: if status_filter:
title += f" ({status_filter.capitalize()})" title += f" ({status_filter.capitalize()})"
if game_filter: if game_filter:
@ -467,13 +467,13 @@ def drop_campaign_list_view(request: HttpRequest) -> HttpResponse: # noqa: PLR0
except Game.DoesNotExist: except Game.DoesNotExist:
pass pass
description = "Browse Twitch drop campaigns" description = "Browse Twitch drops"
if status_filter == "active": if status_filter == "active":
description = "Browse active Twitch drop campaigns." description = "Browse active Twitch drops."
elif status_filter == "upcoming": elif status_filter == "upcoming":
description = "View upcoming Twitch drop campaigns starting soon." description = "View upcoming Twitch drops starting soon."
elif status_filter == "expired": elif status_filter == "expired":
description = "Browse expired Twitch drop campaigns." description = "Browse expired Twitch drops."
# Build base URL for pagination # Build base URL for pagination
base_url = "/campaigns/" base_url = "/campaigns/"
@ -965,7 +965,7 @@ class GameDetailView(DetailView):
owners: list[Organization] = list(game.owners.all()) owners: list[Organization] = list(game.owners.all())
game_name: str = game.display_name or game.name or game.twitch_id game_name: str = game.display_name or game.name or game.twitch_id
game_description: str = f"Twitch drop campaigns for {game_name}." game_description: str = f"Twitch drops for {game_name}."
game_image: str | None = game.box_art_best_url game_image: str | None = game.box_art_best_url
game_image_width: int | None = game.box_art_width if game.box_art_file else None game_image_width: int | None = game.box_art_width if game.box_art_file else None
game_image_height: int | None = ( game_image_height: int | None = (
@ -1129,7 +1129,7 @@ def dashboard(request: HttpRequest) -> HttpResponse:
seo_context: dict[str, Any] = _build_seo_context( seo_context: dict[str, Any] = _build_seo_context(
page_title="Twitch Drops", page_title="Twitch Drops",
page_description="Overview of active Twitch drop campaigns and rewards.", page_description="Overview of active Twitch drops and rewards.",
seo_meta={ seo_meta={
"og_type": "website", "og_type": "website",
"schema_data": website_schema, "schema_data": website_schema,
@ -1186,17 +1186,17 @@ def reward_campaign_list_view(request: HttpRequest) -> HttpResponse:
except EmptyPage: except EmptyPage:
reward_campaigns = paginator.page(paginator.num_pages) reward_campaigns = paginator.page(paginator.num_pages)
title = "Twitch Reward Campaigns" title = "Twitch Rewards"
if status_filter: if status_filter:
title += f" ({status_filter.capitalize()})" title += f" ({status_filter.capitalize()})"
description = "Twitch rewards." description = "Twitch rewards."
if status_filter == "active": if status_filter == "active":
description = "Browse active Twitch reward campaigns." description = "Browse active Twitch rewards."
elif status_filter == "upcoming": elif status_filter == "upcoming":
description = "Browse upcoming Twitch reward campaigns." description = "Browse upcoming Twitch rewards."
elif status_filter == "expired": elif status_filter == "expired":
description = "Browse expired Twitch reward campaigns." description = "Browse expired Twitch rewards."
# Build base URL for pagination # Build base URL for pagination
base_url = "/reward-campaigns/" base_url = "/reward-campaigns/"