mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2025-01-26 08:58:07 +00:00
Merge pull request #1865 from bookwyrm-social/unread-status-translation
Calculate and translate unread status counts in view
This commit is contained in:
commit
40a14a05ad
8 changed files with 75 additions and 59 deletions
|
@ -14,7 +14,7 @@ VERSION = "0.2.0"
|
||||||
PAGE_LENGTH = env("PAGE_LENGTH", 15)
|
PAGE_LENGTH = env("PAGE_LENGTH", 15)
|
||||||
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
|
DEFAULT_LANGUAGE = env("DEFAULT_LANGUAGE", "English")
|
||||||
|
|
||||||
JS_CACHE = "76c5ff1f"
|
JS_CACHE = "7b5303af"
|
||||||
|
|
||||||
# email
|
# email
|
||||||
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
EMAIL_BACKEND = env("EMAIL_BACKEND", "django.core.mail.backends.smtp.EmailBackend")
|
||||||
|
|
|
@ -122,39 +122,13 @@ let BookWyrm = new (class {
|
||||||
*/
|
*/
|
||||||
updateCountElement(counter, data) {
|
updateCountElement(counter, data) {
|
||||||
let count = data.count;
|
let count = data.count;
|
||||||
const count_by_type = data.count_by_type;
|
|
||||||
|
if (count === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const currentCount = counter.innerText;
|
const currentCount = counter.innerText;
|
||||||
const hasMentions = data.has_mentions;
|
const hasMentions = data.has_mentions;
|
||||||
const allowedStatusTypesEl = document.getElementById("unread-notifications-wrapper");
|
|
||||||
|
|
||||||
// If we're on the right counter element
|
|
||||||
if (counter.closest("[data-poll-wrapper]").contains(allowedStatusTypesEl)) {
|
|
||||||
const allowedStatusTypes = JSON.parse(allowedStatusTypesEl.textContent);
|
|
||||||
|
|
||||||
// For keys in common between allowedStatusTypes and count_by_type
|
|
||||||
// This concerns 'review', 'quotation', 'comment'
|
|
||||||
count = allowedStatusTypes.reduce(function (prev, currentKey) {
|
|
||||||
const currentValue = count_by_type[currentKey] | 0;
|
|
||||||
|
|
||||||
return prev + currentValue;
|
|
||||||
}, 0);
|
|
||||||
|
|
||||||
// Add all the "other" in count_by_type if 'everything' is allowed
|
|
||||||
if (allowedStatusTypes.includes("everything")) {
|
|
||||||
// Clone count_by_type with 0 for reviews/quotations/comments
|
|
||||||
const count_by_everything_else = Object.assign({}, count_by_type, {
|
|
||||||
review: 0,
|
|
||||||
quotation: 0,
|
|
||||||
comment: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
count = Object.keys(count_by_everything_else).reduce(function (prev, currentKey) {
|
|
||||||
const currentValue = count_by_everything_else[currentKey] | 0;
|
|
||||||
|
|
||||||
return prev + currentValue;
|
|
||||||
}, count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count != currentCount) {
|
if (count != currentCount) {
|
||||||
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-hidden", count < 1);
|
this.addRemoveClass(counter.closest("[data-poll-wrapper]"), "is-hidden", count < 1);
|
||||||
|
@ -517,7 +491,7 @@ let BookWyrm = new (class {
|
||||||
|
|
||||||
duplicateInput(event) {
|
duplicateInput(event) {
|
||||||
const trigger = event.currentTarget;
|
const trigger = event.currentTarget;
|
||||||
const input_id = trigger.dataset["duplicate"];
|
const input_id = trigger.dataset.duplicate;
|
||||||
const orig = document.getElementById(input_id);
|
const orig = document.getElementById(input_id);
|
||||||
const parent = orig.parentNode;
|
const parent = orig.parentNode;
|
||||||
const new_count = parent.querySelectorAll("input").length + 1;
|
const new_count = parent.querySelectorAll("input").length + 1;
|
||||||
|
|
|
@ -24,9 +24,12 @@
|
||||||
|
|
||||||
{# announcements and system messages #}
|
{# announcements and system messages #}
|
||||||
{% if not activities.number > 1 %}
|
{% if not activities.number > 1 %}
|
||||||
<a href="{{ request.path }}" class="transition-y is-hidden notification is-primary is-block" data-poll-wrapper>
|
<a
|
||||||
{% blocktrans with tab_key=tab.key %}load <span data-poll="stream/{{ tab_key }}">0</span> unread status(es){% endblocktrans %}
|
href="{{ request.path }}"
|
||||||
{{ allowed_status_types|json_script:"unread-notifications-wrapper" }}
|
class="transition-y is-hidden notification is-primary is-block"
|
||||||
|
data-poll-wrapper
|
||||||
|
>
|
||||||
|
<span data-poll="stream/{{ tab.key }}"></span>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
{% if request.user.show_goal and not goal and tab.key == 'home' %}
|
{% if request.user.show_goal and not goal and tab.key == 'home' %}
|
||||||
|
|
|
@ -45,31 +45,51 @@ class UpdateViews(TestCase):
|
||||||
data = json.loads(result.getvalue())
|
data = json.loads(result.getvalue())
|
||||||
self.assertEqual(data["count"], 1)
|
self.assertEqual(data["count"], 1)
|
||||||
|
|
||||||
def test_get_unread_status_count(self):
|
def test_get_unread_status_string(self):
|
||||||
"""there are so many views, this just makes sure it LOADS"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
|
|
||||||
with patch(
|
with patch(
|
||||||
"bookwyrm.activitystreams.ActivityStream.get_unread_count"
|
"bookwyrm.activitystreams.ActivityStream.get_unread_count"
|
||||||
) as mock_count:
|
) as mock_count, patch(
|
||||||
with patch(
|
"bookwyrm.activitystreams.ActivityStream.get_unread_count_by_status_type"
|
||||||
# pylint:disable=line-too-long
|
) as mock_count_by_status:
|
||||||
"bookwyrm.activitystreams.ActivityStream.get_unread_count_by_status_type"
|
mock_count.return_value = 3
|
||||||
) as mock_count_by_status:
|
mock_count_by_status.return_value = {"review": 5}
|
||||||
mock_count.return_value = 3
|
result = views.get_unread_status_string(request, "home")
|
||||||
mock_count_by_status.return_value = {"review": 5}
|
|
||||||
result = views.get_unread_status_count(request, "home")
|
|
||||||
|
|
||||||
self.assertIsInstance(result, JsonResponse)
|
self.assertIsInstance(result, JsonResponse)
|
||||||
data = json.loads(result.getvalue())
|
data = json.loads(result.getvalue())
|
||||||
self.assertEqual(data["count"], 3)
|
self.assertEqual(data["count"], "Load 5 unread statuses")
|
||||||
self.assertEqual(data["count_by_type"]["review"], 5)
|
|
||||||
|
|
||||||
def test_get_unread_status_count_invalid_stream(self):
|
def test_get_unread_status_string_with_filters(self):
|
||||||
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
|
self.local_user.feed_status_types = ["comment", "everything"]
|
||||||
|
request = self.factory.get("")
|
||||||
|
request.user = self.local_user
|
||||||
|
|
||||||
|
with patch(
|
||||||
|
"bookwyrm.activitystreams.ActivityStream.get_unread_count"
|
||||||
|
) as mock_count, patch(
|
||||||
|
"bookwyrm.activitystreams.ActivityStream.get_unread_count_by_status_type"
|
||||||
|
) as mock_count_by_status:
|
||||||
|
mock_count.return_value = 3
|
||||||
|
mock_count_by_status.return_value = {
|
||||||
|
"generated_note": 1,
|
||||||
|
"comment": 1,
|
||||||
|
"review": 10,
|
||||||
|
}
|
||||||
|
result = views.get_unread_status_string(request, "home")
|
||||||
|
|
||||||
|
self.assertIsInstance(result, JsonResponse)
|
||||||
|
data = json.loads(result.getvalue())
|
||||||
|
self.assertEqual(data["count"], "Load 2 unread statuses")
|
||||||
|
|
||||||
|
def test_get_unread_status_string_invalid_stream(self):
|
||||||
"""there are so many views, this just makes sure it LOADS"""
|
"""there are so many views, this just makes sure it LOADS"""
|
||||||
request = self.factory.get("")
|
request = self.factory.get("")
|
||||||
request.user = self.local_user
|
request.user = self.local_user
|
||||||
|
|
||||||
with self.assertRaises(Http404):
|
with self.assertRaises(Http404):
|
||||||
views.get_unread_status_count(request, "fish")
|
views.get_unread_status_string(request, "fish")
|
||||||
|
|
|
@ -47,7 +47,9 @@ urlpatterns = [
|
||||||
re_path(r"^ostatus_subscribe/?$", views.ostatus_follow_request),
|
re_path(r"^ostatus_subscribe/?$", views.ostatus_follow_request),
|
||||||
# polling updates
|
# polling updates
|
||||||
re_path("^api/updates/notifications/?$", views.get_notification_count),
|
re_path("^api/updates/notifications/?$", views.get_notification_count),
|
||||||
re_path("^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_count),
|
re_path(
|
||||||
|
"^api/updates/stream/(?P<stream>[a-z]+)/?$", views.get_unread_status_string
|
||||||
|
),
|
||||||
# authentication
|
# authentication
|
||||||
re_path(r"^login/?$", views.Login.as_view(), name="login"),
|
re_path(r"^login/?$", views.Login.as_view(), name="login"),
|
||||||
re_path(r"^login/(?P<confirmed>confirmed)/?$", views.Login.as_view(), name="login"),
|
re_path(r"^login/(?P<confirmed>confirmed)/?$", views.Login.as_view(), name="login"),
|
||||||
|
|
|
@ -114,7 +114,7 @@ from .rss_feed import RssFeed
|
||||||
from .search import Search
|
from .search import Search
|
||||||
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
from .status import CreateStatus, EditStatus, DeleteStatus, update_progress
|
||||||
from .status import edit_readthrough
|
from .status import edit_readthrough
|
||||||
from .updates import get_notification_count, get_unread_status_count
|
from .updates import get_notification_count, get_unread_status_string
|
||||||
from .user import User, Followers, Following, hide_suggestions, user_redirect
|
from .user import User, Followers, Following, hide_suggestions, user_redirect
|
||||||
from .wellknown import *
|
from .wellknown import *
|
||||||
from .annual_summary import (
|
from .annual_summary import (
|
||||||
|
|
|
@ -62,7 +62,6 @@ class Feed(View):
|
||||||
"streams": STREAMS,
|
"streams": STREAMS,
|
||||||
"goal_form": forms.GoalForm(),
|
"goal_form": forms.GoalForm(),
|
||||||
"feed_status_types_options": FeedFilterChoices,
|
"feed_status_types_options": FeedFilterChoices,
|
||||||
"allowed_status_types": request.user.feed_status_types,
|
|
||||||
"filters_applied": filters_applied,
|
"filters_applied": filters_applied,
|
||||||
"path": f"/{tab['key']}",
|
"path": f"/{tab['key']}",
|
||||||
"annual_summary_year": get_annual_summary_year(),
|
"annual_summary_year": get_annual_summary_year(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
""" endpoints for getting updates about activity """
|
""" endpoints for getting updates about activity """
|
||||||
from django.contrib.auth.decorators import login_required
|
from django.contrib.auth.decorators import login_required
|
||||||
from django.http import Http404, JsonResponse
|
from django.http import Http404, JsonResponse
|
||||||
|
from django.utils.translation import ngettext
|
||||||
|
|
||||||
from bookwyrm import activitystreams
|
from bookwyrm import activitystreams
|
||||||
|
|
||||||
|
@ -17,14 +18,31 @@ def get_notification_count(request):
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def get_unread_status_count(request, stream="home"):
|
def get_unread_status_string(request, stream="home"):
|
||||||
"""any unread statuses for this feed?"""
|
"""any unread statuses for this feed?"""
|
||||||
stream = activitystreams.streams.get(stream)
|
stream = activitystreams.streams.get(stream)
|
||||||
if not stream:
|
if not stream:
|
||||||
raise Http404
|
raise Http404
|
||||||
return JsonResponse(
|
|
||||||
{
|
counts_by_type = stream.get_unread_count_by_status_type(request.user).items()
|
||||||
"count": stream.get_unread_count(request.user),
|
if counts_by_type == {}:
|
||||||
"count_by_type": stream.get_unread_count_by_status_type(request.user),
|
count = stream.get_unread_count(request.user)
|
||||||
}
|
else:
|
||||||
)
|
# only consider the types that are visible in the feed
|
||||||
|
allowed_status_types = request.user.feed_status_types
|
||||||
|
count = sum(c for (k, c) in counts_by_type if k in allowed_status_types)
|
||||||
|
# if "everything else" is allowed, add other types to the sum
|
||||||
|
count += sum(
|
||||||
|
c
|
||||||
|
for (k, c) in counts_by_type
|
||||||
|
if k not in ["review", "comment", "quotation"]
|
||||||
|
)
|
||||||
|
|
||||||
|
if not count:
|
||||||
|
return JsonResponse({})
|
||||||
|
|
||||||
|
translation_string = lambda c: ngettext(
|
||||||
|
"Load %(count)d unread status", "Load %(count)d unread statuses", c
|
||||||
|
) % {"count": c}
|
||||||
|
|
||||||
|
return JsonResponse({"count": translation_string(count)})
|
||||||
|
|
Loading…
Reference in a new issue