Add to and edit lists

This commit is contained in:
Mouse Reeve 2021-01-31 10:34:25 -08:00
parent 4883231347
commit acd922970a
8 changed files with 149 additions and 60 deletions

View File

@ -0,0 +1,45 @@
{% csrf_token %}
<input type="hidden" name="user" value="{{ request.user.id }}">
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="id_name">Name:</label>
{{ list_form.name }}
</div>
<div class="field">
<label class="label" for="id_description">Description:</label>
{{ list_form.description }}
</div>
</div>
<div class="column">
<fieldset class="field">
<legend class="label">List curation:</legend>
<label class="field">
<input type="radio" name="curation" value="closed"{% if not list or list.curation == 'closed' %} checked{% endif %}> Closed
<p class="help mb-2">Only you can add and remove books to this list</p>
</label>
<label class="field">
<input type="radio" name="curation" value="curated"{% if list.curation == 'curated' %} checked{% endif %}> Curated
<p class="help mb-2">Anyone can suggest books, subject to your approval</p>
</label>
<label class="field">
<input type="radio" name="curation" value="open"{% if list.curation == 'open' %} checked{% endif %}> Open
<p class="help mb-2">Anyone can add books to this list, but only you can remove them</p>
</label>
</fieldset>
</div>
</div>
<div class="field has-addons">
<div class="control">
{% include 'snippets/privacy_select.html' with current=list.privacy %}
</div>
<div class="control">
<button type="submit" class="button is-primary">Save</button>
{% include 'snippets/toggle/close_button.html' with controls_text='create-list' text="Cancel" %}
</div>
</div>

View File

@ -1,28 +1,66 @@
{% extends 'layout.html' %} {% extends 'layout.html' %}
{% load bookwyrm_tags %}
{% block content %} {% block content %}
<header class="columns content"> <header class="columns content">
<div class="column"> <div class="column">
<h1 class="title">{{ list.name }} <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span></h1> <h1 class="title">{{ list.name }} <span class="subtitle">{% include 'snippets/privacy-icons.html' with item=list %}</span></h1>
<p class="subtitle help">Created by {% include 'snippets/username.html' with user=list.user %}</p>
{% include 'snippets/trimmed_text.html' with full=list.description %} {% include 'snippets/trimmed_text.html' with full=list.description %}
</div> </div>
{% if request.user == list.user %}
<div class="column is-narrow"> <div class="column is-narrow">
{% include 'snippets/toggle/open_button.html' with text="Edit list" icon="pencil" controls_text="edit-list" %} {% include 'snippets/toggle/open_button.html' with text="Edit list" icon="pencil" controls_text="create-list" %}
</div> </div>
{% endif %}
</header> </header>
<form name="create-list" method="post" action="{% url 'list' list.id %}" class="box hidden" id="create-list">
<h3 class="title">Edit list</h3>
{% include 'lists/form.html' %}
</form>
<div class="columns content"> <div class="columns content">
<section class="column"> <section class="column is-three-quarters">
<h2>Books</h2>
{% if not list.books.exists %} {% if not list.books.exists %}
<p>This list is currently empty</p> <p>This list is currently empty</p>
{% else %} {% else %}
{% for book in list.books.all %} <ol>
{{ book }} {% for item in list.listitem_set.all %}
<li class="block">
<div class="card">
<header class="card-header">
<a href="{{ book.local_path }}">{% include 'snippets/book_cover.html' with book=item.book size="medium" %}</a>
<div class="card-header-title is-flex-direction-column is-align-items-self-start">
<span>{% include 'snippets/book_titleby.html' with book=item.book %}</span>
{% include 'snippets/stars.html' with rating=item.book|rating:request.user %}
{% include 'snippets/shelve_button.html' with book=item.book %}
</div>
</header>
{% if item.note %}
<div class="card-content">
{% include 'snippets/trimmed_text.html' with full=item.note %}
</div>
{% endif %}
<div class="card-footer has-background-white-bis">
<div class="card-footer-item">
<p>Added by {% include 'snippets/username.html' with user=item.added_by %}</p>
</div>
<form name="add-book" method="post" action="{% url 'list-add-book' list.id %}" class="card-footer-item">
{% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<button type="submit" class="button is-small is-danger">Remove</button>
</form>
</div>
</div>
</li>
{% endfor %} {% endfor %}
</ol>
{% endif %} {% endif %}
</section> </section>
{% if not list.curation == 'closed' or request.user == list.user %}
<section class="column is-one-quarter"> <section class="column is-one-quarter">
<h2>Add Books</h2> <h2>Add Books</h2>
{% for book in suggested_books %} {% for book in suggested_books %}
@ -34,11 +72,13 @@
<p>{% include 'snippets/book_titleby.html' with book=book %}</p> <p>{% include 'snippets/book_titleby.html' with book=book %}</p>
<form name="add-book" method="post" action="{% url 'list-add-book' list.id %}"> <form name="add-book" method="post" action="{% url 'list-add-book' list.id %}">
{% csrf_token %} {% csrf_token %}
<input type="hidden" name="book" value="{{ book.id }}">
<button type="submit" class="button is-small is-link">Add</button> <button type="submit" class="button is-small is-link">Add</button>
</form> </form>
</div> </div>
</div> </div>
{% endfor %} {% endfor %}
</section> </section>
{% endif %}
</div> </div>
{% endblock %} {% endblock %}

View File

@ -18,50 +18,7 @@
<form name="create-list" method="post" action="{% url 'lists' %}" class="box hidden" id="create-list"> <form name="create-list" method="post" action="{% url 'lists' %}" class="box hidden" id="create-list">
<h3 class="title">Create list</h3> <h3 class="title">Create list</h3>
{% csrf_token %} {% include 'lists/form.html' %}
<input type="hidden" name="user" value="{{ request.user.id }}">
<div class="columns">
<div class="column">
<div class="field">
<label class="label" for="id_name">Name:</label>
{{ list_form.name }}
</div>
<div class="field">
<label class="label" for="id_description">Description:</label>
{{ list_form.description }}
</div>
</div>
<div class="column">
<fieldset class="field">
<legend class="label">List curation:</legend>
<label class="field">
<input type="radio" name="curation" value="closed" checked> Closed
<p class="help mb-2">Only you can add and remove books to this list</p>
</label>
<label class="field">
<input type="radio" name="curation" value="curated"> Curated
<p class="help mb-2">Anyone can suggest books, subject to your approval</p>
</label>
<label class="field">
<input type="radio" name="curation" value="open"> Open
<p class="help mb-2">Anyone can add books to this list, but only you can remove them</p>
</label>
</fieldset>
</div>
</div>
<div class="field has-addons">
<div class="control">
{% include 'snippets/privacy_select.html' %}
</div>
<div class="control">
<button type="submit" class="button is-primary">Save</button>
{% include 'snippets/toggle/close_button.html' with controls_text='create-list' text="Cancel" %}
</div>
</div>
</form> </form>
{% if request.user.list_set.exists %} {% if request.user.list_set.exists %}

View File

@ -13,7 +13,7 @@
<label class="is-sr-only" for="rating-no-rating-{{ book.id }}">No rating</label> <label class="is-sr-only" for="rating-no-rating-{{ book.id }}">No rating</label>
<input class="is-sr-only" type="radio" name="rating" value="" id="rating-no-rating-{{ book.id }}" checked> <input class="is-sr-only" type="radio" name="rating" value="" id="rating-no-rating-{{ book.id }}" checked>
{% for i in '12345'|make_list %} {% for i in '12345'|make_list %}
<input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|rating:user == forloop.counter %}checked{% endif %}> <input class="is-sr-only" id="rating-book{{book.id}}-star-{{ forloop.counter }}" type="radio" name="rating" value="{{ forloop.counter }}" {% if book|user_rating:user == forloop.counter %}checked{% endif %}>
<label class="icon icon-star-empty" for="rating-book{{book.id}}-star-{{ forloop.counter }}"> <label class="icon icon-star-empty" for="rating-book{{book.id}}-star-{{ forloop.counter }}">
<span class="is-sr-only">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span> <span class="is-sr-only">{{ forloop.counter }} star{{ forloop.counter | pluralize }}</span>
</label> </label>

View File

@ -1,8 +1,7 @@
<div class="stars"> <p class="stars">
<span class="is-sr-only">{% if rating %}{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}{% else %}No rating{% endif %}</span> <span class="is-sr-only">{% if rating %}{{ rating|floatformat }} star{{ rating|floatformat | pluralize }}{% else %}No rating{% endif %}</span>
{% for i in '12345'|make_list %} {% for i in '12345'|make_list %}
<span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}"> <span class="icon icon-star-{% if rating >= forloop.counter %}full{% elif rating|floatformat:0 >= forloop.counter|floatformat:0 %}half{% else %}empty{% endif %}" aria-hidden="true">
</span> </span>
{% endfor %} {% endfor %}
</div> </p>

View File

@ -4,9 +4,10 @@ from datetime import datetime
from dateutil.relativedelta import relativedelta from dateutil.relativedelta import relativedelta
from django import template from django import template
from django.db.models import Avg
from django.utils import timezone from django.utils import timezone
from bookwyrm import models from bookwyrm import models, views
from bookwyrm.views.status import to_markdown from bookwyrm.views.status import to_markdown
@ -20,6 +21,17 @@ def dict_key(d, k):
@register.filter(name='rating') @register.filter(name='rating')
def get_rating(book, user): def get_rating(book, user):
''' get the overall rating of a book '''
queryset = views.helpers.get_activity_feed(
user,
['public', 'followers', 'unlisted', 'direct'],
queryset=models.Review.objects.filter(book=book),
)
return queryset.aggregate(Avg('rating'))['rating__avg']
@register.filter(name='user_rating')
def get_user_rating(book, user):
''' get a user's rating of a book ''' ''' get a user's rating of a book '''
rating = models.Review.objects.filter( rating = models.Review.objects.filter(
user=user, user=user,

View File

@ -91,7 +91,7 @@ urlpatterns = [
re_path(r'^list/(?P<list_id>\d+)(.json)?/?$', re_path(r'^list/(?P<list_id>\d+)(.json)?/?$',
views.List.as_view(), name='list'), views.List.as_view(), name='list'),
re_path(r'^list/(?P<list_id>\d+)/add/?$', re_path(r'^list/(?P<list_id>\d+)/add/?$',
views.List.as_view(), name='list-add-book'), views.list.add_book, name='list-add-book'),
# preferences # preferences
re_path(r'^preferences/profile/?$', views.EditUser.as_view()), re_path(r'^preferences/profile/?$', views.EditUser.as_view()),

View File

@ -1,11 +1,12 @@
''' book list views''' ''' book list views'''
from django.contrib.auth.decorators import login_required from django.contrib.auth.decorators import login_required
from django.db.models import Q from django.db.models import Q
from django.http import HttpResponseNotFound from django.http import HttpResponseNotFound, HttpResponseBadRequest
from django.shortcuts import get_object_or_404, redirect from django.shortcuts import get_object_or_404, redirect
from django.template.response import TemplateResponse from django.template.response import TemplateResponse
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.views import View from django.views import View
from django.views.decorators.http import require_POST
from bookwyrm import forms, models from bookwyrm import forms, models
from bookwyrm.activitypub import ActivitypubResponse from bookwyrm.activitypub import ActivitypubResponse
@ -35,7 +36,6 @@ class Lists(View):
''' create a book_list ''' ''' create a book_list '''
form = forms.ListForm(request.POST) form = forms.ListForm(request.POST)
if not form.is_valid(): if not form.is_valid():
print(form.errors)
return redirect('lists') return redirect('lists')
book_list = form.save() book_list = form.save()
return redirect(book_list.local_path) return redirect(book_list.local_path)
@ -52,14 +52,15 @@ class List(View):
if is_api_request(request): if is_api_request(request):
return ActivitypubResponse(book_list.to_activity()) return ActivitypubResponse(book_list.to_activity())
suggestions = request.user.shelfbook_set.all().filter( suggestions = request.user.shelfbook_set.filter(
~Q(book__in=book_list.books) ~Q(book__in=book_list.books.all())
) )
data = { data = {
'title': '%s | Lists' % book_list.name, 'title': '%s | Lists' % book_list.name,
'list': book_list, 'list': book_list,
'suggested_books': [s.book for s in suggestions[:5]], 'suggested_books': [s.book for s in suggestions[:5]],
'list_form': forms.ListForm(instance=book_list),
} }
return TemplateResponse(request, 'lists/list.html', data) return TemplateResponse(request, 'lists/list.html', data)
@ -69,4 +70,39 @@ class List(View):
def post(self, request, list_id): def post(self, request, list_id):
''' edit a book_list ''' ''' edit a book_list '''
book_list = get_object_or_404(models.List, id=list_id) book_list = get_object_or_404(models.List, id=list_id)
form = forms.ListForm(request.POST, instance=book_list)
if not form.is_valid():
return redirect('list', book_list.id)
book_list = form.save()
return redirect(book_list.local_path) return redirect(book_list.local_path)
@require_POST
def add_book(request, list_id):
''' put a book on a list '''
book_list = get_object_or_404(models.List, id=list_id)
if not object_visible_to_user(request.user, book_list):
return HttpResponseNotFound()
book = get_object_or_404(models.Edition, id=request.POST.get('book'))
# do you have permission to add to the list?
if request.user == book_list.user or book_list.curation == 'open':
# go ahead and add it
models.ListItem.objects.create(
book=book,
book_list=book_list,
added_by=request.user,
)
elif book_list.curation == 'curated':
# make a pending entry
models.ListItem.objects.create(
approved=False,
book=book,
book_list=book_list,
added_by=request.user,
)
else:
# you can't add to this list, what were you THINKING
return HttpResponseBadRequest()
return redirect('list', list_id)