Skip to content

Commit

Permalink
Add mark as read to categories page, tweak mark as read action
Browse files Browse the repository at this point in the history
  • Loading branch information
rafalp committed Aug 31, 2024
1 parent 41d4276 commit 960ba28
Show file tree
Hide file tree
Showing 16 changed files with 350 additions and 30 deletions.
9 changes: 4 additions & 5 deletions frontend/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export class Misago {
bulkModeration(options) {
return new BulkModeration(options)
}

hideMarkAsReadModal = () => {
$("#mark-as-read").modal("hide")
}
}

// create the singleton
Expand Down Expand Up @@ -120,8 +124,3 @@ document.addEventListener("htmx:afterRequest", ({ target }) => {
document.addEventListener("misago:afterModeration", () => {
$("#threads-moderation-modal").modal("hide")
})

// Hide mark as read modal
document.addEventListener("misago:afterMarkAsRead", () => {
$("#mark-as-read").modal("hide")
})
24 changes: 24 additions & 0 deletions misago/categories/tests/test_categories_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,30 @@ def test_categories_view_displays_default_category_for_user(
assert_contains(response, default_category.description)


@override_dynamic_settings(index_view="threads")
def test_categories_view_displays_for_guest_in_htmx(default_category, client):
default_category.description = "FIRST-CATEGORY-DESCRIPTION"
default_category.save()

response = client.get(
reverse("misago:categories"),
headers={"hx-request": "true"},
)
assert_contains(response, default_category.description)


@override_dynamic_settings(index_view="threads")
def test_categories_view_displays_for_user_in_htmx(default_category, user_client):
default_category.description = "FIRST-CATEGORY-DESCRIPTION"
default_category.save()

response = user_client.get(
reverse("misago:categories"),
headers={"hx-request": "true"},
)
assert_contains(response, default_category.description)


@override_dynamic_settings(index_view="threads")
def test_categories_view_excludes_default_category_for_guest_without_permission(
default_category, guests_group, client
Expand Down
77 changes: 77 additions & 0 deletions misago/categories/tests/test_mark_as_read.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
from django.urls import reverse

from ...conf.test import override_dynamic_settings
from ...readtracker.models import ReadCategory, ReadThread
from ...test import assert_contains


@override_dynamic_settings(index_view="threads")
def test_categories_view_mark_as_read_displays_categories_for_guests(
default_category, client
):
default_category.description = "FIRST-CATEGORY-DESCRIPTION"
default_category.save()

response = client.post(reverse("misago:categories"), {"mark_as_read": "true"})
assert_contains(response, default_category.description)


@override_dynamic_settings(index_view="threads")
def test_categories_view_mark_as_read_displays_confirmation_page_to_users(user_client):
response = user_client.post(reverse("misago:categories"), {"mark_as_read": "true"})
assert_contains(response, "Mark as read | Categories")
assert_contains(response, "Are you sure you want to mark all categories as read?")


@override_dynamic_settings(index_view="threads")
def test_categories_view_mark_as_read_marks_categories_read(
user_client, default_category, user, thread
):
ReadThread.objects.create(
user=user,
category=default_category,
thread=thread,
read_time=thread.last_post_on,
)

response = user_client.post(
reverse("misago:categories"), {"mark_as_read": "true", "confirm": "true"}
)
assert response.status_code == 302
assert response["location"] == reverse("misago:categories")

ReadCategory.objects.get(
user=user,
category=default_category,
)

assert not ReadThread.objects.exists()


@override_dynamic_settings(index_view="threads")
def test_categories_view_mark_as_read_marks_categories_read_in_htmx(
user_client, default_category, user, thread
):
default_category.description = "FIRST-CATEGORY-DESCRIPTION"
default_category.save()

ReadThread.objects.create(
user=user,
category=default_category,
thread=thread,
read_time=thread.last_post_on,
)

response = user_client.post(
reverse("misago:categories"),
{"mark_as_read": "true", "confirm": "true"},
headers={"hx-request": "true"},
)
assert_contains(response, default_category.description)

ReadCategory.objects.get(
user=user,
category=default_category,
)

assert not ReadThread.objects.exists()
60 changes: 58 additions & 2 deletions misago/categories/views.py
Original file line number Diff line number Diff line change
@@ -1,27 +1,83 @@
from django.http import HttpRequest
from typing import TYPE_CHECKING

from django.contrib import messages
from django.db import transaction
from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect, render
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import pgettext

from ..metatags.metatags import get_forum_index_metatags
from ..readtracker.models import ReadCategory, ReadThread
from .hooks import (
get_categories_page_component_hook,
get_categories_page_metatags_hook,
)
from .components import get_categories_data

if TYPE_CHECKING:
from ..users.models import User


def index(request, *args, is_index: bool | None = None, **kwargs):
if not is_index and request.settings.index_view == "categories":
return redirect(reverse("misago:index"))

if (
request.method == "POST"
and "mark_as_read" in request.POST
and request.user.is_authenticated
):
if response := mark_as_read(request):
return response

context = {
"is_index": is_index,
"categories_list": get_categories_page_component(request),
}

context["metatags"] = get_categories_page_metatags(request, context)

return render(request, "misago/categories/index.html", context)
if request.is_htmx:
template_name = "misago/categories/partial.html"
else:
template_name = "misago/categories/index.html"

return render(request, template_name, context)


def mark_as_read(request: HttpRequest) -> HttpResponse | None:
if not request.POST.get("confirm"):
return render(request, "misago/categories/mark_as_read_page.html")

if categories_ids := list(request.categories.categories):
read_all_categories(request.user, categories_ids)
messages.success(
request, pgettext("mark categories as read", "Categories marked as read")
)

if request.is_htmx:
return None

if request.settings.index_view == "categories":
return redirect(reverse("misago:index"))

return redirect(reverse("misago:categories"))


@transaction.atomic
def read_all_categories(user: "User", categories_ids: list[int]):
read_time = timezone.now()

# Clear read tracker for categories
ReadThread.objects.filter(user=user, category_id__in=categories_ids).delete()
ReadCategory.objects.filter(user=user, category_id__in=categories_ids).delete()

ReadCategory.objects.bulk_create(
ReadCategory(user=user, read_time=read_time, category_id=category_id)
for category_id in categories_ids
)


def get_categories_page_component(request: HttpRequest) -> dict:
Expand Down
2 changes: 1 addition & 1 deletion misago/static/misago/js/misago.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion misago/static/misago/js/misago.js.map

Large diffs are not rendered by default.

18 changes: 11 additions & 7 deletions misago/templates/misago/categories/index.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{% extends "misago/base.html" %}
{% load i18n misago_absoluteurl misago_component misago_plugins misago_shorthands %}
{% load i18n misago_absoluteurl misago_plugins misago_shorthands %}


{% block title %}
Expand Down Expand Up @@ -84,16 +84,20 @@

{% pluginoutlet CATEGORIES_LIST_START %}

{% if categories_list.categories %}
{% includecomponent categories_list %}
{% else %}
{% include "misago/categories/blankslate.html" %}
{% endif %}
<div id="misago-page-scroll-target" class="scroll-target"></div>
{% include "misago/categories/partial.html" %}

{% pluginoutlet CATEGORIES_LIST_END %}

{% include "misago/gototop_toolbar.html" %}

</div>
</div>
{% endblock content %}
{% endblock content %}


{% block modals %}
{% if user.is_authenticated %}
{% include "misago/categories/mark_as_read_modal.html" %}
{% endif %}
{% endblock modals %}
8 changes: 8 additions & 0 deletions misago/templates/misago/categories/mark_as_read_modal.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{% extends "misago/threads/mark_as_read_modal.html" %}
{% load i18n %}

{% block modal-body %}
{% blocktrans trimmed context "mark categories as read" %}
Are you sure you want to mark all categories as read?
{% endblocktrans %}
{% endblock modal-body %}
18 changes: 18 additions & 0 deletions misago/templates/misago/categories/mark_as_read_page.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{% extends "misago/threads/mark_as_read_page.html" %}
{% load i18n %}

{% block title %}
{% trans "Mark as read" context "mark categories as read" %} | {% trans "Categories" context "categories page" %} | {{ settings.forum_name }}
{% endblock title %}

{% block page-class %}page-categories{% endblock page-class %}

{% block header %}
{% include "misago/categories/header.html" %}
{% endblock header %}

{% block panel-body %}
{% blocktrans trimmed context "mark categories as read" %}
Are you sure you want to mark all categories as read?
{% endblocktrans %}
{% endblock panel-body %}
14 changes: 14 additions & 0 deletions misago/templates/misago/categories/partial.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{% load misago_component %}

{% if is_request_htmx %}
{% include "misago/snackbars.html" %}
{% endif %}

<div id="misago-htmx-root">
{% if categories_list.categories %}
{% includecomponent categories_list %}
{% include "misago/categories/toolbar.html" %}
{% else %}
{% include "misago/categories/blankslate.html" %}
{% endif %}
</div>
27 changes: 27 additions & 0 deletions misago/templates/misago/categories/toolbar.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{% load i18n %}

{% if user.is_authenticated %}
<div class="toolbar">
<div class="toolbar-spacer"></div>
<div class="toolbar-section">
<div class="toolbar-item">
<noscript>
<form method="post">
{% csrf_token %}
<button class="btn btn-default btn-block" name="mark_as_read">
{% trans "Mark as read" context "categories list toolbar" %}
</button>
</form>
</noscript>
<button
class="btn btn-default d-js-block"
type="button"
data-toggle="modal"
data-target="#mark-as-read"
>
{% trans "Mark as read" context "categories list toolbar" %}
</button>
</div>
</div>
</div>
{% endif %}
6 changes: 4 additions & 2 deletions misago/templates/misago/category/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@


{% block modals %}
{% include "misago/category/mark_as_read_modal.html" %}
{% include "misago/threads/moderation.html" %}
{% if user.is_authenticated %}
{% include "misago/category/mark_as_read_modal.html" %}
{% include "misago/threads/moderation.html" %}
{% endif %}
{% endblock modals %}
6 changes: 4 additions & 2 deletions misago/templates/misago/threads/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@

{% block modals %}
{% include "misago/threads/start_thread.html" %}
{% include "misago/threads/mark_as_read_modal.html" %}
{% include "misago/threads/moderation.html" %}
{% if user.is_authenticated %}
{% include "misago/threads/mark_as_read_modal.html" %}
{% include "misago/threads/moderation.html" %}
{% endif %}
{% endblock modals %}
1 change: 1 addition & 0 deletions misago/templates/misago/threads/mark_as_read_modal.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
hx-post="{{ full_path }}"
hx-target="#misago-htmx-root"
hx-swap="outerHTML show:#misago-page-scroll-target:top"
hx-on::after-request="misago.hideMarkAsReadModal()"
>
{% csrf_token %}
<input type="hidden" name="confirm" value="true" />
Expand Down
Loading

0 comments on commit 960ba28

Please sign in to comment.