diff --git a/accounts/views.py b/accounts/views.py
index 4a4f890..735db1d 100644
--- a/accounts/views.py
+++ b/accounts/views.py
@@ -1,6 +1,6 @@
from __future__ import annotations
-from typing import TYPE_CHECKING, ClassVar
+from typing import TYPE_CHECKING
from django.contrib.auth import login
from django.contrib.auth.decorators import login_required
@@ -11,6 +11,7 @@ from django.views.generic import CreateView
from accounts.forms import CustomUserCreationForm
from accounts.models import User
+from twitch.models import NotificationSubscription
if TYPE_CHECKING:
from django.forms import BaseModelForm
@@ -36,7 +37,7 @@ class CustomLogoutView(LogoutView):
"""Custom logout view."""
next_page = reverse_lazy("twitch:dashboard")
- http_method_names: ClassVar[list[str]] = ["get", "post", "options"] # pyright: ignore[reportIncompatibleVariableOverride]
+ http_method_names = ["get", "post", "options"]
def get(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
"""Allow GET requests for logout.
@@ -84,4 +85,12 @@ def profile_view(request: HttpRequest) -> HttpResponse:
Returns:
HttpResponse: Rendered profile template.
"""
- return render(request, "accounts/profile.html", {"user": request.user})
+ subscriptions = NotificationSubscription.objects.filter(user=request.user)
+ return render(
+ request,
+ "accounts/profile.html",
+ {
+ "user": request.user,
+ "subscriptions": subscriptions,
+ },
+ )
diff --git a/pyproject.toml b/pyproject.toml
index 297126e..50520e8 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -37,6 +37,8 @@ lint.pydocstyle.convention = "google"
lint.isort.required-imports = ["from __future__ import annotations"]
lint.ignore = [
+ "ANN002", # Checks that function *args arguments have type annotations.
+ "ANN003", # Checks that function **kwargs arguments have type annotations.
"CPY001", # Checks for the absence of copyright notices within Python files.
"D100", # Checks for undocumented public module definitions.
"D104", # Checks for undocumented public package definitions.
@@ -45,8 +47,7 @@ lint.ignore = [
"ERA001", # Checks for commented-out Python code.
"FIX002", # Checks for "TODO" comments.
"PLR6301", # Checks for the presence of unused self parameter in methods definitions.
- "ANN002", # Checks that function *args arguments have type annotations.
- "ANN003", # Checks that function **kwargs arguments have type annotations.
+ "RUF012", # Checks for mutable default values in class attributes.
# Conflicting lint rules when using Ruff's formatter
# https://docs.astral.sh/ruff/formatter/#conflicting-lint-rules
diff --git a/templates/accounts/profile.html b/templates/accounts/profile.html
index 1bce3ed..c6dc6be 100644
--- a/templates/accounts/profile.html
+++ b/templates/accounts/profile.html
@@ -26,4 +26,19 @@
Logout
+
Will get notifications to:
+
{% endblock content %}
diff --git a/templates/twitch/campaign_detail.html b/templates/twitch/campaign_detail.html
index 9952918..aa79fde 100644
--- a/templates/twitch/campaign_detail.html
+++ b/templates/twitch/campaign_detail.html
@@ -9,7 +9,7 @@
{# TODO: Link to organization #}
- {{ campaign.owner.name }}
+ {{ campaign.owner.name }}
{% if campaign.image_url %}
{{ game.display_name }}
+ {% if owner %}
+ {{ owner.name }}
+ {% endif %}
{% if user.is_authenticated %}
diff --git a/twitch/models.py b/twitch/models.py
index 86a75da..404b63f 100644
--- a/twitch/models.py
+++ b/twitch/models.py
@@ -216,4 +216,8 @@ class NotificationSubscription(models.Model):
]
def __str__(self) -> str:
- return f"{self.user} subscription to {Game.display_name}"
+ if self.game:
+ return f"{self.user} subscription to game: {self.game.display_name}"
+ if self.organization:
+ return f"{self.user} subscription to organization: {self.organization.name}"
+ return f"{self.user} subscription"
diff --git a/twitch/urls.py b/twitch/urls.py
index 14e392f..35fa48b 100644
--- a/twitch/urls.py
+++ b/twitch/urls.py
@@ -14,6 +14,6 @@ urlpatterns = [
path("games//", views.GameDetailView.as_view(), name="game_detail"),
path("games//subscribe/", views.subscribe_game_notifications, name="subscribe_notifications"),
path("organizations/", views.OrgListView.as_view(), name="org_list"),
- path("organizations//", views.OrgDetailView.as_view(), name="org_detail"),
+ path("organizations//", views.OrgDetailView.as_view(), name="organization_detail"),
path("organizations//subscribe/", views.subscribe_org_notifications, name="subscribe_org_notifications"),
]
diff --git a/twitch/views.py b/twitch/views.py
index 8c8e05f..e168bb2 100644
--- a/twitch/views.py
+++ b/twitch/views.py
@@ -49,10 +49,21 @@ class OrgDetailView(DetailView):
Returns:
dict: Context data.
"""
- organization: Organization = self.object
context = super().get_context_data(**kwargs)
- games = Game.objects.filter(drop_campaigns__owner=organization).distinct()
- context["games"] = games
+ organization: Organization = self.object
+
+ user = self.request.user
+ if not user.is_authenticated:
+ subscription: NotificationSubscription | None = None
+ else:
+ subscription = NotificationSubscription.objects.filter(user=user, organization=organization).first()
+
+ games: QuerySet[Game, Game] = Game.objects.filter(drop_campaigns__owner=organization).distinct()
+ context.update({
+ "subscription": subscription,
+ "games": games,
+ })
+
return context
@@ -290,6 +301,7 @@ class GameDetailView(DetailView):
"upcoming_campaigns": upcoming_campaigns,
"expired_campaigns": expired_campaigns,
"subscription": subscription,
+ "owner": active_campaigns[0].owner if active_campaigns else None,
"now": now,
})
@@ -430,7 +442,7 @@ def subscribe_org_notifications(request: HttpRequest, org_id: str) -> HttpRespon
message = ""
messages.success(request, message)
- return redirect("organization_detail", org_id=organization.id)
+ return redirect("twitch:organization_detail", pk=organization.id)
messages.warning(request, "Only POST is available for this view.")
- return redirect("organization_detail", org_id=organization.id)
+ return redirect("twitch:organization_detail", pk=organization.id)