diff options
-rw-r--r-- | elisp/grocy.el | 106 |
1 files changed, 45 insertions, 61 deletions
diff --git a/elisp/grocy.el b/elisp/grocy.el index c5ec6d6..55998de 100644 --- a/elisp/grocy.el +++ b/elisp/grocy.el @@ -21,14 +21,11 @@ (require 'url) (require 'dash) +(require 'vtable) ;; Silence byte-compiler. (defvar url-http-end-of-headers) -(defvar grocy-units) -(defvar grocy-locations) -(defvar grocy-product-groups) - (defcustom grocy-server "" "Grocy main domain." :type 'string @@ -39,6 +36,10 @@ :type 'string :group 'grocy) +(defvar grocy-units (make-hash-table)) +(defvar grocy-locations (make-hash-table)) +(defvar grocy-product-groups (make-hash-table)) + (defun grocy--request (endpoint callback &optional method data) "Do a GET request to api ENDPOINT and call CALLBACK on the resulting buffer. Set METHOD to other request types and include the already encoded DATA." @@ -70,7 +71,7 @@ Set METHOD to other request types and include the already encoded DATA." (grocy-update-info "product_groups" grocy-product-groups "name") (defun grocy-stock--row (row) - "Compose ROW into tabulated entry for stock view." + "Compose ROW into vtable object for stock view." (-let* (((&alist 'amount 'amount_opened 'product 'best_before_date) row) ((&alist 'name 'qu_id_stock) product) (amount @@ -83,7 +84,7 @@ Set METHOD to other request types and include the already encoded DATA." (concat " / " (number-to-string amount_opened) " open"))))) - (list row (vector best_before_date amount name)))) + (list best_before_date amount name))) (defun grocy-stock () "Show the grocery stock." @@ -94,59 +95,45 @@ Set METHOD to other request types and include the already encoded DATA." (goto-char url-http-end-of-headers) (let ((result (json-parse-buffer :object-type 'alist))) (with-current-buffer (get-buffer-create "Grocy stock") - (tabulated-list-mode) - (setq tabulated-list-format [("Next due" 12 t) - ("Amount" 20 t) - ("Product" 20 t)] - ;; tabulated-list-padding 2 - tabulated-list-sort-key '("Next due")) - - (thread-last - (cl-map 'list #'grocy-stock--row result) - (setq tabulated-list-entries)) - (tabulated-list-init-header) - (tabulated-list-print) + (erase-buffer) + (make-vtable + :columns '((:name "Next due" :primary ascend) "Amount" "Product") + :row-colors (list "gray12" "gray6") + :objects (cl-map 'list #'grocy-stock--row result)) (display-buffer (current-buffer))))))) -(defun grocy-products--row (product) - (-let* (((&hash "name" "location_id" "qu_id_stock" - "min_stock_amount" "product_group_id") product) - (prod-group (gethash product_group_id grocy-product-groups))) - (list product - (vector name - (gethash location_id grocy-locations) - (car (gethash qu_id_stock grocy-units)) - (number-to-string min_stock_amount) - (if prod-group prod-group "NULL" ))))) - -(defun grocy-products--refresh () +(defun grocy-products () + "Render vtable view of products." + (interactive) (grocy--request "objects/products" (lambda (_status) (goto-char url-http-end-of-headers) (let ((result (json-parse-buffer))) (with-current-buffer (get-buffer-create "Grocy products") - (setq tabulated-list-entries - (cl-map 'list #'grocy-products--row result)) - (tabulated-list-print 'remember t)))))) - -(defun grocy-products () - "Render tabulated list view of products." - (interactive) - (with-current-buffer (get-buffer-create "Grocy products") - (tabulated-list-mode) - (setq tabulated-list-format [("Name" 40 t) - ("Location" 25 t) - ("Unit" 8 t) - ("min stock" 5 t) - ("Group" 10 t)] - tabulated-list-sort-key '("Name")) - (local-set-key "e" #'grocy-update-product) - (local-set-key "a" #'grocy-add-product-shopping-list) - (add-hook 'tabulated-list-revert-hook #'grocy-products--refresh nil t) - (run-hooks 'tabulated-list-revert-hook) - (tabulated-list-init-header) - (display-buffer (current-buffer)))) + (erase-buffer) + (make-vtable + :columns `((:name "Name" :primary ascend + :getter ,(apply-partially #'gethash "name")) + "Location" "Unit" + (:name "min" + :getter ,(apply-partially #'gethash "min_stock_amount")) + "Group") + :actions '("e" grocy-update-product + "a" grocy-add-product-shopping-list) + :row-colors (list "gray12" "gray6") + :objects (seq-into result 'list) + :getter + (lambda (object column vtable) + (pcase (vtable-column vtable column) + ("Location" (thread-first (gethash "location_id" object) + (gethash grocy-locations))) + ("Unit" (thread-first (gethash "qu_id_stock" object) + (gethash grocy-units) car)) + ("Group" (thread-first + (gethash "product_group_id" object) + (gethash grocy-product-groups "NULL")))))) + (display-buffer (current-buffer))))))) (defun grocy--message-error-reply (status) "Display `url-retrieve' response buffer and error STATUS." @@ -168,12 +155,11 @@ Set METHOD to other request types and include the already encoded DATA." (grocy-buffer-obj->json (current-buffer))) (kill-buffer))) -(defun grocy-add-product-shopping-list () - "Add selected product to first shopping list." +(defun grocy-add-product-shopping-list (product) + "Add PRODUCT to first shopping list." (interactive) - (let* ((product (tabulated-list-get-id)) - (id (gethash "id" product)) - (name (gethash "name" product))) + (let ((id (gethash "id" product)) + (name (gethash "name" product))) (with-current-buffer (get-buffer-create "* Grocy add product to shopping list *") (erase-buffer) (emacs-lisp-mode) @@ -183,11 +169,9 @@ Set METHOD to other request types and include the already encoded DATA." (grocy-push-buffer-and-kill-fn "stock/shoppinglist/add-product" "POST")) (switch-to-buffer (current-buffer))))) -(defun grocy-update-product () - "Update form for currently selected product." - (interactive) - (let* ((product (tabulated-list-get-id)) - (id (gethash "id" product))) +(defun grocy-update-product (product) + "Update form for PRODUCT." + (let ((id (gethash "id" product))) (with-current-buffer (get-buffer-create "* Grocy update product *") (erase-buffer) (thread-first |