diff --git a/common/static/lib/css/collection.css b/common/static/lib/css/collection.css index 97b09040..c9524c16 100644 --- a/common/static/lib/css/collection.css +++ b/common/static/lib/css/collection.css @@ -114,17 +114,17 @@ div.jsoneditor-menu { @keyframes fadeIn { 0% {opacity: 0;} 100% {opacity: 1;} -} +} @keyframes fadeOut { 0% {opacity: 1;} 100% {opacity: 0;} -} +} @keyframes zoomIn { 0% {transform: scale(0.9);} 100% {transform: scale(1);} -} +} @keyframes zoomOut { 0% {transform: scale(1);} @@ -159,3 +159,25 @@ div.jsoneditor-menu { #modal li, #modal ul, #modal label { display: inline; } + +.donut { + margin-left: 20%; + width: 60%; + aspect-ratio : 1 / 1; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; +} +.donut .hole { + width: 80%; + aspect-ratio : 1 / 1; + border-radius: 50%; + background: #f7f7f7; + display: table; +} +.donut .text { + display: table-cell; + vertical-align: middle; + text-align: center; +} diff --git a/common/templates/partial/_sidebar.html b/common/templates/partial/_sidebar.html index be94ec4a..7a878d8b 100644 --- a/common/templates/partial/_sidebar.html +++ b/common/templates/partial/_sidebar.html @@ -5,6 +5,7 @@ {% load oauth_token %} {% load truncate %} {% load thumb %} +{% load collection %}
@@ -43,11 +44,47 @@
+ {% if user.featured_collections %} +
+
+ +
+
+ {% trans '当前目标' %} +
+ {% for featured_collection in user.featured_collections.all %} + {% user_visibility_of featured_collection as visible %} + {% if visible %} + {% user_progress_of collection=featured_collection user=user as progress %} +
+ {{ featured_collection.collection.title }}
+ 已完成{{ progress }}%
+ +
+ {% endif %} + {% endfor %} +
+
+
+
+ {% endif %} + {% if user == request.user %}
-
- +
{% trans '关注的人' %} @@ -100,7 +137,7 @@ {% endif %}
- +
{% if request.user.is_staff and request.user == user%} diff --git a/journal/migrations/0005_auto_20230114_1134.py b/journal/migrations/0005_auto_20230114_1134.py new file mode 100644 index 00000000..00d2e9f8 --- /dev/null +++ b/journal/migrations/0005_auto_20230114_1134.py @@ -0,0 +1,58 @@ +# Generated by Django 3.2.16 on 2023-01-14 03:34 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("journal", "0004_alter_shelflogentry_timestamp"), + ] + + operations = [ + migrations.CreateModel( + name="FeaturedCollection", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("created_time", models.DateTimeField(auto_now_add=True)), + ("edited_time", models.DateTimeField(auto_now=True)), + ( + "collection", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="journal.collection", + ), + ), + ( + "owner", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "unique_together": {("owner", "collection")}, + }, + ), + migrations.AddField( + model_name="collection", + name="featured_by_users", + field=models.ManyToManyField( + related_name="featured_collections", + through="journal.FeaturedCollection", + to=settings.AUTH_USER_MODEL, + ), + ), + ] diff --git a/journal/models.py b/journal/models.py index e9440463..50baa15a 100644 --- a/journal/models.py +++ b/journal/models.py @@ -679,6 +679,9 @@ class Collection(List): collaborative = models.PositiveSmallIntegerField( default=0 ) # 0: Editable by owner only / 1: Editable by bi-direction followers + featured_by_users = models.ManyToManyField( + to=User, related_name="featured_collections", through="FeaturedCollection" + ) @property def html(self): @@ -690,6 +693,26 @@ class Collection(List): html = markdown(self.brief) return _RE_HTML_TAG.sub(" ", html) + def is_featured_by_user(self, user): + return self.featured_by_users.all().filter(id=user.id).exists() + + def get_stats_for_user(self, user): + items = list(self.members.all().values_list("item_id", flat=True)) + stats = {"total": len(items)} + for st, shelf in user.shelf_manager.shelf_list.items(): + stats[st] = shelf.members.all().filter(item_id__in=items).count() + stats["percentage"] = ( + round(stats["complete"] * 100 / stats["total"]) if stats["total"] else 0 + ) + return stats + + def get_progress_for_user(self, user): + items = list(self.members.all().values_list("item_id", flat=True)) + if len(items) == 0: + return 0 + shelf = user.shelf_manager.shelf_list["complete"] + return shelf.members.all().filter(item_id__in=items).count() * 100 / len(items) + def save(self, *args, **kwargs): if getattr(self, "catalog_item", None) is None: self.catalog_item = CatalogCollection() @@ -704,6 +727,16 @@ class Collection(List): super().save(*args, **kwargs) +class FeaturedCollection(models.Model): + owner = models.ForeignKey(User, on_delete=models.CASCADE) + collection = models.ForeignKey(Collection, on_delete=models.CASCADE) + created_time = models.DateTimeField(auto_now_add=True) + edited_time = models.DateTimeField(auto_now=True) + + class Meta: + unique_together = [["owner", "collection"]] + + """ Tag """ diff --git a/journal/templates/collection.html b/journal/templates/collection.html index 23e06655..7f46f180 100644 --- a/journal/templates/collection.html +++ b/journal/templates/collection.html @@ -62,7 +62,7 @@ {% endif %}
- + {{ collection.html | safe }}
@@ -91,7 +91,46 @@
- {% if request.user.is_authenticated and request.user != collection.owner %} + {% if is_featured %} +
+
+
+ {% if stats.progress %} + {{ stats.progress }} 进行中
+ {% endif %} + {% if stats.complete %} + {{ stats.complete }} 已完成 + {% elif not stats.progress %} + 尚未开始 + {% endif %} +
+
+ +
+
+
+ {% csrf_token %} + +
+
+
+
+ {% endif %} + + {% if available_as_featured %} +
+
+
+
+ {% csrf_token %} + +
+
+
+
+ {% endif %} + + {% if not is_featured and request.user.is_authenticated and request.user != collection.owner %}
@@ -105,7 +144,7 @@ {% csrf_token %} - {% endif %} + {% endif %}
diff --git a/journal/templatetags/collection.py b/journal/templatetags/collection.py new file mode 100644 index 00000000..b1b61bfc --- /dev/null +++ b/journal/templatetags/collection.py @@ -0,0 +1,18 @@ +from django import template +from journal.models import Collection, Like +from django.shortcuts import reverse + +register = template.Library() + + +@register.simple_tag(takes_context=True) +def user_visibility_of(context, piece): + user = context["request"].user + return piece.is_visible_to(user) + + +@register.simple_tag() +def user_progress_of(collection, user): + return ( + collection.get_progress_for_user(user) if user and user.is_authenticated else 0 + ) diff --git a/journal/urls.py b/journal/urls.py index 1abb78cc..74892409 100644 --- a/journal/urls.py +++ b/journal/urls.py @@ -70,6 +70,16 @@ urlpatterns = [ collection_update_item_note, name="collection_update_item_note", ), + path( + "collection//add_featured", + collection_add_featured, + name="collection_add_featured", + ), + path( + "collection//remove_featured", + collection_remove_featured, + name="collection_remove_featured", + ), re_path( r"^users/(?P[A-Za-z0-9_\-.@]+)/(?P" + _get_all_shelf_types() diff --git a/journal/views.py b/journal/views.py index 4fcd67ea..ffd5ed4e 100644 --- a/journal/views.py +++ b/journal/views.py @@ -1,4 +1,5 @@ import logging +from os import stat from django.shortcuts import render, get_object_or_404, redirect from django.urls import reverse from django.contrib.auth.decorators import login_required, permission_required @@ -179,6 +180,27 @@ def collection_retrieve(request, collection_uuid): if request.user.is_authenticated else False ) + is_featured = request.user.is_authenticated and collection.is_featured_by_user( + request.user + ) + available_as_featured = ( + request.user.is_authenticated + and (following or request.user == collection.owner) + and not is_featured + and collection.members.all().exists() + ) + stats = {} + if is_featured: + stats = collection.get_stats_for_user(request.user) + stats["wishlist_deg"] = ( + stats["wishlist"] / stats["total"] * 360 if stats["total"] else 0 + ) + stats["progress_deg"] = ( + stats["progress"] / stats["total"] * 360 if stats["total"] else 0 + ) + stats["complete_deg"] = ( + stats["complete"] / stats["total"] * 360 if stats["total"] else 0 + ) return render( request, "collection.html", @@ -186,10 +208,33 @@ def collection_retrieve(request, collection_uuid): "collection": collection, "follower_count": follower_count, "following": following, + "stats": stats, + "available_as_featured": available_as_featured, + "is_featured": is_featured, }, ) +def collection_add_featured(request, collection_uuid): + if request.method != "POST": + return HttpResponseBadRequest() + collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid)) + if not collection.is_visible_to(request.user): + raise PermissionDenied() + request.user.featured_collections.add(collection) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + + +def collection_remove_featured(request, collection_uuid): + if request.method != "POST": + return HttpResponseBadRequest() + collection = get_object_or_404(Collection, uid=base62.decode(collection_uuid)) + if not collection.is_visible_to(request.user): + raise PermissionDenied() + request.user.featured_collections.remove(collection) + return HttpResponseRedirect(request.META.get("HTTP_REFERER")) + + def collection_share(request, collection_uuid): pass diff --git a/users/data.py b/users/data.py index baae3528..1b613baa 100644 --- a/users/data.py +++ b/users/data.py @@ -28,7 +28,7 @@ from django.contrib import messages from journal.importers.douban import DoubanImporter from journal.importers.goodreads import GoodreadsImporter -from journal.models import reset_visibility_for_user +from journal.models import reset_visibility_for_user, Collection @mastodon_request_included @@ -54,7 +54,11 @@ def preferences(request): "show_last_edit", ] ) - return render(request, "users/preferences.html") + return render( + request, + "users/preferences.html", + {"collections": Collection.objects.filter(owner=request.user)}, + ) @mastodon_request_included diff --git a/users/templates/users/preferences.html b/users/templates/users/preferences.html index bea82baa..3a2b3c98 100644 --- a/users/templates/users/preferences.html +++ b/users/templates/users/preferences.html @@ -27,11 +27,11 @@
+ {% csrf_token %}
{% trans '使用偏好设置' %}
- {% csrf_token %} {% trans '新标记默认可见性:' %}