changeset 1395:4ccf42d8aab7

browser (lists/view): `grocery` list specialised behaviour: if the list is a `grocery` list, user can change status directly in list view, delete an item, and do a quick item creation without having to use the `create` button.
author Goffi <goffi@goffi.org>
date Sat, 27 Feb 2021 21:08:42 +0100
parents 72f9639594b2
children 822bd0139769
files libervia/pages/lists/view/_browser/__init__.py libervia/pages/lists/view/page_meta.py
diffstat 2 files changed, 128 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/libervia/pages/lists/view/_browser/__init__.py	Sat Feb 27 21:06:45 2021 +0100
+++ b/libervia/pages/lists/view/_browser/__init__.py	Sat Feb 27 21:08:42 2021 +0100
@@ -1,10 +1,15 @@
-from browser import window, bind
+from browser import window, document, aio, bind
 from invitation import InvitationManager
+from javascript import JSON
+from aio_bridge import Bridge, BridgeException
+import dialog
 
 
+bridge = Bridge()
 lists_ns = window.lists_ns
 pubsub_service = window.pubsub_service
 pubsub_node = window.pubsub_node
+list_type = window.list_type
 try:
     affiliations = window.affiliations.to_dict()
 except AttributeError:
@@ -29,3 +34,100 @@
             pubsub_data['name'] = name
     manager = InvitationManager("pubsub", pubsub_data)
     manager.attach(affiliations=affiliations)
+
+
+async def on_delete(evt):
+    item_elt = evt.target.closest(".item")
+    if item_elt is None:
+        dialog.notification.show(
+            "Can't find parent item element",
+            level="error"
+        )
+        return
+    item_elt.classList.add("selected_for_deletion")
+    item = JSON.parse(item_elt.dataset.item)
+    confirmed = await dialog.Confirm(
+        f"{item['name']!r} will be deleted, are you sure?",
+        ok_label="delete",
+        ok_color="danger",
+    ).ashow()
+    item_elt.classList.remove("selected_for_deletion")
+    if confirmed:
+        try:
+            await bridge.psItemRetract(pubsub_service, pubsub_node, item["id"], True)
+        except Exception as e:
+            dialog.notification.show(
+                f"Can't delete list item: {e}",
+                level="error"
+            )
+        else:
+            dialog.notification.show("list item deleted successfuly")
+            item_elt.remove()
+
+
+async def on_next_state(evt):
+    """Update item with next state
+
+    Only used with grocery list at the moment
+    """
+    evt.stopPropagation()
+    evt.preventDefault()
+    # FIXME: states are currently hardcoded, it would be better to use schema
+    item_elt = evt.target.closest(".item")
+    if item_elt is None:
+        dialog.notification.show(
+            "Can't find parent item element",
+            level="error"
+        )
+        return
+    item = JSON.parse(item_elt.dataset.item)
+    try:
+        status = item["status"]
+    except (KeyError, IndexError) as e:
+        dialog.notification.show(
+            f"Can't get item status: {e}",
+            level="error"
+        )
+        status = "to_buy"
+    if status == "to_buy":
+        item["status"] = "bought"
+        class_update_method = item_elt.classList.add
+        checked = True
+    elif status == "bought":
+        item["status"] = "to_buy"
+        checked = False
+        class_update_method = item_elt.classList.remove
+    else:
+        print(status)
+        dialog.notification.show(
+            f"unexpected item status: {status!r}",
+            level="error"
+        )
+        return
+    item_elt.dataset.item = JSON.stringify(item)
+    try:
+        await bridge.listSet(
+            pubsub_service,
+            pubsub_node,
+            {k: [v] for k,v in item.items()},
+            "",
+            item["id"],
+            ""
+        )
+    except BridgeException as e:
+        dialog.notification.show(
+            f"Can't udate list item: {e.message}",
+            level="error"
+        )
+    else:
+        evt.target.checked = checked
+        class_update_method("list-item-closed")
+
+
+if list_type == "grocery":
+    for elt in document.select('.click_to_delete'):
+        elt.bind("click", lambda evt: aio.run(on_delete(evt)))
+
+    for elt in document.select('.click_to_next_state'):
+        elt.bind("click", lambda evt: aio.run(on_next_state(evt)))
+
--- a/libervia/pages/lists/view/page_meta.py	Sat Feb 27 21:06:45 2021 +0100
+++ b/libervia/pages/lists/view/page_meta.py	Sat Feb 27 21:08:42 2021 +0100
@@ -119,3 +119,28 @@
         pubsub_node=node,
         list_type=list_type,
     )
+
+
+async def on_data_post(self, request):
+    data = self.getRData(request)
+    profile = self.getProfile(request)
+    service = data["service"]
+    node = data["node"]
+    list_type = self.getPostedData(request, ("type",))
+    if list_type == "grocery":
+        name, quantity = self.getPostedData(request, ("name", "quantity"))
+        if not name:
+            self.pageError(request, C.HTTP_BAD_REQUEST)
+        item_data = {
+            "name": [name],
+        }
+        if quantity:
+            item_data["quantity"] = [quantity]
+        await self.host.bridgeCall(
+            "listSet", service.full(), node, item_data, "", "", "", profile
+        )
+        return C.POST_NO_CONFIRM
+    else:
+        raise NotImplementedError(
+            f"Can't use quick list item set for list of type {list_type!r}"
+        )