v0.7.0: mark all as read by clicking the unread badge

- Click a source's unread count in the sidebar to mark all of that source's
  messages read (clears the badge + reduces the All-sources total); click the
  All-sources total to mark everything read.
- POST /feed/mark-read (session, owner-scoped, excludes trashed). Visible feed
  rows drop their unread state too. Badge is keyboard-activatable (Enter/Space).

156 tests pass (4 new); inline JS re-validated with node --check.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
peter 2026-09-07 22:26:48 +01:00
parent 7b161f1276
commit 89936ebf58
7 changed files with 123 additions and 5 deletions

View file

@ -13,6 +13,7 @@ urlpatterns = [
path("a/<str:artifact_id>/delete", views.delete_artifact, name="artifact-delete"),
path("a/<str:artifact_id>/undelete", views.undelete_artifact, name="artifact-undelete"),
path("feed/empty-trash", views.empty_trash, name="feed-empty-trash"),
path("feed/mark-read", views.mark_all_read, name="feed-mark-read"),
path("a/<str:artifact_id>", views.artifact_view, name="artifact"),
path("s/<str:share_token>", views.shared_artifact_view, name="artifact-shared"),
path("assets/<str:asset_id>", views.asset_view, name="asset"),

View file

@ -696,6 +696,21 @@ def empty_trash(request):
return JsonResponse({"purged": count})
@login_required
@require_POST
def mark_all_read(request):
"""Mark all of the user's unread (non-trashed) messages as read, optionally
scoped to one source. Backs the sidebar 'Mark all as read' action."""
unread = Artifact.objects.filter(
owner=request.user, deleted_at__isnull=True, read_at__isnull=True
)
source = request.POST.get("source", "")
if source:
unread = unread.filter(source=source)
marked = unread.update(read_at=timezone.now())
return JsonResponse({"marked": marked, "source": source})
@login_required
def feed_older(request):
"""Return the next page of older day-groups as an HTML fragment for infinite scroll."""