diff --git a/feedvault/settings.py b/feedvault/settings.py index d030d3f..a7724cc 100644 --- a/feedvault/settings.py +++ b/feedvault/settings.py @@ -65,6 +65,7 @@ INSTALLED_APPS: list[str] = [ "django.contrib.sessions", "django.contrib.messages", "django.contrib.sitemaps", + "django_htmx", ] MIDDLEWARE: list[str] = [ @@ -77,6 +78,7 @@ MIDDLEWARE: list[str] = [ "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django_htmx.middleware.HtmxMiddleware", ] # Use PostgreSQL as the default database diff --git a/feedvault/views.py b/feedvault/views.py index a42339d..fbaef57 100644 --- a/feedvault/views.py +++ b/feedvault/views.py @@ -13,6 +13,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.views import LoginView, LogoutView, PasswordChangeView from django.contrib.messages.views import SuccessMessageMixin from django.core.exceptions import SuspiciousOperation +from django.core.paginator import EmptyPage, Paginator from django.db.models.manager import BaseManager from django.http import FileResponse, Http404, HttpRequest, HttpResponse from django.shortcuts import get_object_or_404, render @@ -20,7 +21,6 @@ from django.template import loader from django.urls import reverse_lazy from django.views import View from django.views.generic.edit import CreateView -from django.views.generic.list import ListView from feedvault.feeds import add_url from feedvault.models import Domain, Entry, Feed, FeedAddResult, UserUploadedFile @@ -33,6 +33,10 @@ if TYPE_CHECKING: logger: logging.Logger = logging.getLogger(__name__) +class HtmxHttpRequest(HttpRequest): + htmx: Any + + class IndexView(View): """Index path.""" @@ -74,24 +78,33 @@ class FeedView(View): return render(request, "feed.html", context) -class FeedsView(ListView): +class FeedsView(View): """All feeds.""" - model = Feed - paginate_by = 100 - template_name = "feeds.html" - context_object_name = "feeds" + def get(self, request: HtmxHttpRequest) -> HttpResponse: + """All feeds.""" + feeds: BaseManager[Feed] = Feed.objects.only("id", "feed_url") - def get_context_data(self, **kwargs) -> dict: # noqa: ANN003 - """Get the context data.""" - context = super().get_context_data(**kwargs) - feed_amount: int = Feed.objects.count() or 0 - context["description"] = f"Archiving {feed_amount} feeds" - context["keywords"] = "feed, rss, atom, archive, rss list" - context["author"] = "TheLovinator" - context["canonical"] = "https://feedvault.se/feeds/" - context["title"] = "Feeds" - return context + paginator = Paginator(object_list=feeds, per_page=100) + page_number = int(request.GET.get("page", default=1)) + + try: + pages = paginator.get_page(page_number) + except EmptyPage: + return HttpResponse("") + + context = { + "feeds": pages, + "description": "An archive of all feeds", + "keywords": "feed, rss, atom, archive, rss list", + "author": "TheLovinator", + "canonical": "https://feedvault.se/feeds/", + "title": "Feeds", + "page": page_number, + } + + template_name = "partials/feeds.html" if request.htmx else "feeds.html" + return render(request, template_name, context) class AddView(LoginRequiredMixin, View): diff --git a/poetry.lock b/poetry.lock index 5d5d5ea..2f6759d 100644 --- a/poetry.lock +++ b/poetry.lock @@ -463,6 +463,21 @@ files = [ django = ">=3.2.4" sqlparse = ">=0.2" +[[package]] +name = "django-htmx" +version = "1.17.3" +description = "Extensions for using Django with htmx." +optional = false +python-versions = ">=3.8" +files = [ + {file = "django-htmx-1.17.3.tar.gz", hash = "sha256:a2069219920d7ef0883ddbf5e8d931069db145a0d4a8a032a2708f840c7a68a6"}, + {file = "django_htmx-1.17.3-py3-none-any.whl", hash = "sha256:0de964ca257eda2a4ebeeaa8181320119378fa5f95a2fc2f2bfbdd35034ed424"}, +] + +[package.dependencies] +asgiref = ">=3.6" +Django = ">=3.2" + [[package]] name = "django-ninja" version = "1.1.0" @@ -1299,4 +1314,4 @@ brotli = ["Brotli"] [metadata] lock-version = "2.0" python-versions = "^3.12" -content-hash = "67041176bd9ca4289c6900d794465da1df4a966ed81625367552fa238b7976f8" +content-hash = "e21c650c4ac009f1e6ba8939aa6236a63b8dc3a095b803fe7e13a529fc62b072" diff --git a/pyproject.toml b/pyproject.toml index a2b6b78..700ed42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,7 +7,7 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" -django = {extras = ["argon2"], version = "^5.0.3"} +django = { extras = ["argon2"], version = "^5.0.3" } python-dotenv = "^1.0.1" feedparser = "^6.0.11" gunicorn = "^21.2.0" @@ -15,9 +15,10 @@ dateparser = "^1.2.0" discord-webhook = "^1.3.1" django-ninja = "^1.1.0" django-debug-toolbar = "^4.3.0" -whitenoise = {extras = ["brotli"], version = "^6.6.0"} +whitenoise = { extras = ["brotli"], version = "^6.6.0" } rich = "^13.7.1" -psycopg = {extras = ["binary"], version = "^3.1.18"} +psycopg = { extras = ["binary"], version = "^3.1.18" } +django-htmx = "^1.17.3" [tool.poetry.group.dev.dependencies] ruff = "^0.3.0" @@ -25,9 +26,7 @@ djlint = "^1.34.1" [build-system] build-backend = "poetry.core.masonry.api" -requires = [ - "poetry-core", -] +requires = ["poetry-core"] [tool.ruff] exclude = ["migrations"] diff --git a/templates/base.html b/templates/base.html index 193a0d2..86566e7 100644 --- a/templates/base.html +++ b/templates/base.html @@ -9,7 +9,7 @@ {% if author %}{% endif %} {% if canonical %}{% endif %}