Repository: bookwyrm-social/bookwyrm

Issue:
Adding a book to list that already has the book gives vague error
**Describe the bug**
When you try to add a book to a list that the book is already in you receive a message that just says "Server Error, Something went wrong. Sorry about that." 

**To Reproduce**
Steps to reproduce the behavior:
1. Add a book to a list that isn't already in the list. 
2. Add the same book to the same list. 
3. The error appears. 

**Expected behavior**
The error message should explain why there is an error ("This book is already in this list") so that users don't keep trying to add the book. 

**Instance**
https://tankie.ml/

---

**Smartphone (please complete the following information):**
    - Device: Samsung Galaxy A52 5G
    - OS: Android 12
    - Browser: Firefox
    - Version: 97.2.0
    


Discussion:

Resolving patch:
diff --git a/bookwyrm/views/books/books.py b/bookwyrm/views/books/books.py
--- a/bookwyrm/views/books/books.py
+++ b/bookwyrm/views/books/books.py
@@ -83,6 +83,7 @@ def get(self, request, book_id, user_statuses=False):
         }
 
         if request.user.is_authenticated:
+            data["list_options"] = request.user.list_set.exclude(id__in=data["lists"])
             data["file_link_form"] = forms.FileLinkForm()
             readthroughs = models.ReadThrough.objects.filter(
                 user=request.user,
diff --git a/bookwyrm/views/list/list.py b/bookwyrm/views/list/list.py
--- a/bookwyrm/views/list/list.py
+++ b/bookwyrm/views/list/list.py
@@ -1,11 +1,10 @@
 """ book list views"""
 from typing import Optional
-from urllib.parse import urlencode
 
 from django.contrib.auth.decorators import login_required
 from django.core.exceptions import PermissionDenied
 from django.core.paginator import Paginator
-from django.db import IntegrityError, transaction
+from django.db import transaction
 from django.db.models import Avg, DecimalField, Q, Max
 from django.db.models.functions import Coalesce
 from django.http import HttpResponseBadRequest, HttpResponse
@@ -26,7 +25,7 @@
 class List(View):
     """book list page"""
 
-    def get(self, request, list_id):
+    def get(self, request, list_id, add_failed=False, add_succeeded=False):
         """display a book list"""
         book_list = get_object_or_404(models.List, id=list_id)
         book_list.raise_visible_to_user(request.user)
@@ -37,33 +36,10 @@ def get(self, request, list_id):
         query = request.GET.get("q")
         suggestions = None
 
-        # sort_by shall be "order" unless a valid alternative is given
-        sort_by = request.GET.get("sort_by", "order")
-        if sort_by not in ("order", "title", "rating"):
-            sort_by = "order"
-
-        # direction shall be "ascending" unless a valid alternative is given
-        direction = request.GET.get("direction", "ascending")
-        if direction not in ("ascending", "descending"):
-            direction = "ascending"
-
-        directional_sort_by = {
-            "order": "order",
-            "title": "book__title",
-            "rating": "average_rating",
-        }[sort_by]
-        if direction == "descending":
-            directional_sort_by = "-" + directional_sort_by
-
-        items = book_list.listitem_set.prefetch_related("user", "book", "book__authors")
-        if sort_by == "rating":
-            items = items.annotate(
-                average_rating=Avg(
-                    Coalesce("book__review__rating", 0.0),
-                    output_field=DecimalField(),
-                )
-            )
-        items = items.filter(approved=True).order_by(directional_sort_by)
+        items = book_list.listitem_set.filter(approved=True).prefetch_related(
+            "user", "book", "book__authors"
+        )
+        items = sort_list(request, items)
 
         paginated = Paginator(items, PAGE_LENGTH)
 
@@ -106,10 +82,10 @@ def get(self, request, list_id):
             "suggested_books": suggestions,
             "list_form": forms.ListForm(instance=book_list),
             "query": query or "",
-            "sort_form": forms.SortListForm(
-                {"direction": direction, "sort_by": sort_by}
-            ),
+            "sort_form": forms.SortListForm(request.GET),
             "embed_url": embed_url,
+            "add_failed": add_failed,
+            "add_succeeded": add_succeeded,
         }
         return TemplateResponse(request, "lists/list.html", data)
 
@@ -131,6 +107,36 @@ def post(self, request, list_id):
         return redirect(book_list.local_path)
 
 
+def sort_list(request, items):
+    """helper to handle the surprisngly involved sorting"""
+    # sort_by shall be "order" unless a valid alternative is given
+    sort_by = request.GET.get("sort_by", "order")
+    if sort_by not in ("order", "title", "rating"):
+        sort_by = "order"
+
+    # direction shall be "ascending" unless a valid alternative is given
+    direction = request.GET.get("direction", "ascending")
+    if direction not in ("ascending", "descending"):
+        direction = "ascending"
+
+    directional_sort_by = {
+        "order": "order",
+        "title": "book__title",
+        "rating": "average_rating",
+    }[sort_by]
+    if direction == "descending":
+        directional_sort_by = "-" + directional_sort_by
+
+    if sort_by == "rating":
+        items = items.annotate(
+            average_rating=Avg(
+                Coalesce("book__review__rating", 0.0),
+                output_field=DecimalField(),
+            )
+        )
+    return items.order_by(directional_sort_by)
+
+
 @require_POST
 @login_required
 def save_list(request, list_id):
@@ -179,8 +185,8 @@ def add_book(request):
 
     form = forms.ListItemForm(request.POST)
     if not form.is_valid():
-        # this shouldn't happen, there aren't validated fields
-        raise Exception(form.errors)
+        return List().get(request, book_list.id, add_failed=True)
+
     item = form.save(commit=False)
 
     if book_list.curation == "curated":
@@ -196,17 +202,9 @@ def add_book(request):
         ) or 0
         increment_order_in_reverse(book_list.id, order_max + 1)
     item.order = order_max + 1
+    item.save()
 
-    try:
-        item.save()
-    except IntegrityError:
-        # if the book is already on the list, don't flip out
-        pass
-
-    path = reverse("list", args=[book_list.id])
-    params = request.GET.copy()
-    params["updated"] = True
-    return redirect(f"{path}?{urlencode(params)}")
+    return List().get(request, book_list.id, add_succeeded=True)
 
 
 @require_POST


Test patch: