branch: elpa/hyperdrive
commit 047abe89e265ac8e52ddb8a20c11ebc92c00b76d
Merge: 4c5fc6ca76 3fff771e5c
Author: Joseph Turner <jos...@ushin.org>
Commit: Joseph Turner <jos...@ushin.org>

    Merge: Show how much of a file is downloaded in directory view
    
    Also, move entry filling logic into he//api-then to avoid having to
    invoke h//fill in the callback of all requests which download a file
    or clear its blob from the local cache.
---
 CHANGELOG.org         |  2 ++
 doc/hyperdrive.org    | 22 ++++++++++++++----
 doc/hyperdrive.texi   | 28 +++++++++++++++++++----
 hyperdrive-dir.el     | 17 +++++++++-----
 hyperdrive-ewoc.el    | 18 +++++++++++++++
 hyperdrive-history.el | 13 ++++++++---
 hyperdrive-lib.el     | 62 ++++++++++++++++++++++++++++++++++-----------------
 hyperdrive-vars.el    | 11 ++++++++-
 hyperdrive.el         | 31 ++++++++++++--------------
 9 files changed, 149 insertions(+), 55 deletions(-)

diff --git a/CHANGELOG.org b/CHANGELOG.org
index 8367842cce..a151fb59ad 100644
--- a/CHANGELOG.org
+++ b/CHANGELOG.org
@@ -31,6 +31,8 @@ space by "forgetting" your copy of a file 
(~hyperdrive-forget-file~)!
 - View hyperdrive disk usage with ~hyperdrive-describe~ and ~hyperdrive-menu~.
 - Delete the local copy of a file or directory with
   ~hyperdrive-forget-file~, also bound in ~hyperdrive-menu~ and menu bar.
+- Indicate how much of a file has already been downloaded based on the
+  color of its file size in the directory view.
 
 ** Changed
 
diff --git a/doc/hyperdrive.org b/doc/hyperdrive.org
index 320fe36026..3d2bc7bfcf 100644
--- a/doc/hyperdrive.org
+++ b/doc/hyperdrive.org
@@ -221,9 +221,18 @@ hyperdrives you've already created or visited:
 
 *** Directory view
 
-~hyperdrive.el~ offers a Dired-like (see [[info:emacs#Dired]]) interface for
-exploring hyperdrive directories.  The following keybindings are
-available inside the directory view by default:
+~hyperdrive.el~ offers a Dired-like (see [[info:emacs#Dired]]) interface
+for exploring hyperdrive directories.  In the directory view, the file
+size color indicates how much of a file you have already downloaded:
+
+- green :: fully downloaded (~hyperdrive-size-fully-downloaded~)
+- yellow :: partially downloaded (~hyperdrive-size-partially-downloaded~)
+- red :: not downloaded (~hyperdrive-size-not-downloaded~)
+
+Mouse over the file size to see exactly how many blocks make up the
+file and how many of them you have downloaded.
+
+The following bindings are available in the directory view by default:
 
 #+attr_texinfo: :compact t
 - Key: n (hyperdrive-ewoc-next) ::
@@ -463,6 +472,9 @@ This command also has a keybinding in the directory view 
(see
 
 ** Forget a hyperdrive file
 
+*Please note that forgetting a file may result in data loss if it
+cannot be loaded from another peer on the network.*
+
 It is possible to "forget" your local copy of a hyperdrive file in
 order to save disk space.  "Forgetting" a file does not delete the
 file from the hyperdrive and does not increment the hyperdrive's
@@ -473,7 +485,9 @@ version number.
   Delete your local copy of the file for the current buffer.
 
 This command also has a keybinding in the directory view (see
-[[*Directory view]]).
+[[*Directory view]]).  When you forget a file, the file size of its
+directory listing will turn red, indicating that you no longer have a
+copy of the file.
 
 ** View the hyperdrive version history
 
diff --git a/doc/hyperdrive.texi b/doc/hyperdrive.texi
index f787d9c931..73dceeb4d2 100644
--- a/doc/hyperdrive.texi
+++ b/doc/hyperdrive.texi
@@ -46,6 +46,7 @@ Repository: @uref{https://git.sr.ht/~ushin/hyperdrive.el}
 @end itemize
 
 This manual is for @code{hyperdrive.el} version 0.4-pre.
+
 @end ifnottex
 
 @menu
@@ -444,9 +445,23 @@ Like @code{hyperdrive-find-file}, but open the file in 
@ref{View Mode,view-mode,
 @node Directory view
 @subsection Directory view
 
-@code{hyperdrive.el} offers a Dired-like (see @ref{Dired,,,emacs,}) interface 
for
-exploring hyperdrive directories.  The following keybindings are
-available inside the directory view by default:
+@code{hyperdrive.el} offers a Dired-like (see @ref{Dired,,,emacs,}) interface
+for exploring hyperdrive directories.  In the directory view, the file
+size color indicates how much of a file you have already downloaded:
+
+@table @asis
+@item green
+fully downloaded (@code{hyperdrive-size-fully-downloaded})
+@item yellow
+partially downloaded (@code{hyperdrive-size-partially-downloaded})
+@item red
+not downloaded (@code{hyperdrive-size-not-downloaded})
+@end table
+
+Mouse over the file size to see exactly how many blocks make up the
+file and how many of them you have downloaded.
+
+The following bindings are available in the directory view by default:
 
 @table @asis
 @item @kbd{n} (@code{hyperdrive-ewoc-next})
@@ -737,6 +752,9 @@ This command also has a keybinding in the directory view 
(see
 @node Forget a hyperdrive file
 @section Forget a hyperdrive file
 
+@strong{Please note that forgetting a file may result in data loss if it
+cannot be loaded from another peer on the network.}
+
 It is possible to ``forget'' your local copy of a hyperdrive file in
 order to save disk space.  ``Forgetting'' a file does not delete the
 file from the hyperdrive and does not increment the hyperdrive's
@@ -747,7 +765,9 @@ Delete your local copy of the file for the current buffer.
 @end deffn
 
 This command also has a keybinding in the directory view (see
-@ref{Directory view}).
+@ref{Directory view}).  When you forget a file, the file size of its
+directory listing will turn red, indicating that you no longer have a
+copy of the file.
 
 @node View the hyperdrive version history
 @section View the hyperdrive version history
diff --git a/hyperdrive-dir.el b/hyperdrive-dir.el
index 2dd6b622d8..70397ada21 100644
--- a/hyperdrive-dir.el
+++ b/hyperdrive-dir.el
@@ -72,7 +72,6 @@ If THEN, call it in the directory buffer with no arguments."
                                        (json-read-from-string body)
                                        hyperdrive version))
                              (parent-entry (h/parent directory-entry)))
-                  (setf directory-entry (he//fill directory-entry headers))
                   (when parent-entry
                     (setf (alist-get 'display-name (he/etc parent-entry)) 
"../")
                     (push parent-entry entries))
@@ -160,16 +159,24 @@ To be used as the pretty-printer for `ewoc-create'."
 (defun h/dir--format-entry (entry)
   "Return ENTRY formatted as a string."
   (pcase-let*
-      (((cl-struct hyperdrive-entry size mtime) entry)
+      (((cl-struct hyperdrive-entry size mtime etc) entry)
        (size (and size (file-size-human-readable size)))
        (directoryp (h//entry-directory-p entry))
        (face (if directoryp 'h/directory 'default))
        (timestamp (if mtime
                       (format-time-string h/timestamp-format mtime)
-                    (propertize " " 'display '(space :width 
h/timestamp-width)))))
+                    (propertize " " 'display '(space :width 
h/timestamp-width))))
+       ((map block-length block-length-downloaded) etc))
     (format "%6s  %s  %s"
-            (propertize (or size "")
-                        'face 'h/size)
+            (propertize
+             (or size "")
+             'face (and block-length-downloaded block-length
+                        (pcase block-length-downloaded
+                          (0 'h/size-not-downloaded)
+                          ((pred (= block-length)) 'h/size-fully-downloaded)
+                          (_ 'h/size-partially-downloaded)))
+             'help-echo (format "%s of %s blocks downloaded"
+                                block-length-downloaded block-length))
             (propertize timestamp
                         'face 'h/timestamp)
             (propertize (or (alist-get 'display-name (he/etc entry))
diff --git a/hyperdrive-ewoc.el b/hyperdrive-ewoc.el
index 10cc500663..d58e18e5ee 100644
--- a/hyperdrive-ewoc.el
+++ b/hyperdrive-ewoc.el
@@ -51,6 +51,24 @@ last node."
            return node
            do (setf node (ewoc-prev ewoc node))))
 
+(defun he//invalidate (entry)
+  "Invalidate the ewoc node for ENTRY in directory buffers."
+  (when-let* ((buffer (hyperdrive--find-buffer-visiting
+                       (hyperdrive-parent entry)))
+              (ewoc (buffer-local-value 'h/ewoc buffer))
+              (node (and ewoc
+                         (h/ewoc-find-node ewoc entry
+                           :predicate #'he/equal-p))))
+    (when node
+      (ewoc-set-data node entry)
+      ;; NOTE: Ensure that the buffer's window is selected,
+      ;; if it has one.  (Workaround a possible bug in EWOC.)
+      (if-let ((buffer-window (get-buffer-window (ewoc-buffer ewoc))))
+          (with-selected-window buffer-window
+            (with-silent-modifications (ewoc-invalidate ewoc node)))
+        (with-current-buffer (ewoc-buffer ewoc)
+          (with-silent-modifications (ewoc-invalidate ewoc node)))))))
+
 ;;;; Mode
 
 (defvar-keymap h/ewoc-mode-map
diff --git a/hyperdrive-history.el b/hyperdrive-history.el
index b6caaeeeff..0cb074ea79 100644
--- a/hyperdrive-history.el
+++ b/hyperdrive-history.el
@@ -61,7 +61,7 @@ value \\+`unknown', and whose cdr is a hyperdrive entry."
   (pcase-let*
       ((`(,range . ,entry) range-entry)
        (`(,range-start . ,(map :range-end :existsp)) range)
-       ((cl-struct hyperdrive-entry size mtime) entry)
+       ((cl-struct hyperdrive-entry size mtime etc) entry)
        (formatted-range (if (eq range-start range-end)
                             (format "%d" range-start)
                           (format "%d-%d" range-start range-end)))
@@ -72,7 +72,8 @@ value \\+`unknown', and whose cdr is a hyperdrive entry."
        (size (and size (file-size-human-readable size)))
        (timestamp (if mtime
                       (format-time-string h/timestamp-format mtime)
-                    (propertize " " 'display '(space :width 
h/timestamp-width)))))
+                    (propertize " " 'display '(space :width 
h/timestamp-width))))
+       ((map block-length block-length-downloaded) etc))
     ;; FIXME: Use dynamic width of range column equal to 2N+1, where N
     ;; is the width of the hyperdrive's latest version
     (format
@@ -91,7 +92,13 @@ value \\+`unknown', and whose cdr is a hyperdrive entry."
                                       ('unknown "Load history at version %s"))
                                     range-start))
      (propertize (or size "")
-                 'face 'h/size)
+                 'face (and block-length-downloaded block-length
+                            (pcase block-length-downloaded
+                              (0 'h/size-not-downloaded)
+                              ((pred (= block-length)) 
'h/size-fully-downloaded)
+                              (_ 'h/size-partially-downloaded)))
+                 'help-echo (format "%s of %s blocks downloaded"
+                                    block-length-downloaded block-length))
      (propertize (or timestamp "")
                  'face 'h/timestamp))))
 
diff --git a/hyperdrive-lib.el b/hyperdrive-lib.el
index c750dbf0b4..1c3eac480c 100644
--- a/hyperdrive-lib.el
+++ b/hyperdrive-lib.el
@@ -74,7 +74,9 @@ Passes ARGS to `format-message'."
   ;; TODO: Consider adding gv-setters for etc slot keys
   (etc nil :documentation "Alist for extra data about the entry.
 - display-name :: Displayed in directory view instead of name.
-- target :: Link fragment to jump to."))
+- target :: Link fragment to jump to.
+- block-length :: Number of blocks file blob takes up.
+- block-length-downloaded :: Number of blocks downloaded for file."))
 
 (cl-defstruct (hyperdrive (:constructor h/create)
                           (:copier nil))
@@ -228,8 +230,11 @@ Sets ENTRY's hyperdrive to the persisted version of the 
drive if
 it exists.  Updates ENTRY's hyperdrive's disk usage and latest
 version.  Finally, persists ENTRY's hyperdrive."
   (pcase-let*
-      (((cl-struct plz-response
-                   (headers (map link allow x-drive-size x-drive-version)))
+      (((cl-struct
+         plz-response
+         (headers (map link allow content-length content-type last-modified
+                       x-drive-size x-drive-version
+                       x-file-block-length x-file-block-length-downloaded)))
         response)
        ;; RESPONSE is guaranteed to have a "Link" header with the public key,
        ;; while ENTRY may have a DNSLink domain but no public key yet.
@@ -262,7 +267,30 @@ version.  Finally, persists ENTRY's hyperdrive."
             (string-to-number x-drive-version)))
     ;; TODO: Update buffers like h/describe-hyperdrive after updating drive.
     ;; TODO: Consider debouncing or something for hyperdrive-persist to 
minimize I/O.
-    (h/persist (he/hyperdrive entry))))
+    (h/persist (he/hyperdrive entry))
+
+    ;; Fill entry.
+    (when content-length
+      (setf (he/size entry)
+            (ignore-errors (cl-parse-integer content-length))))
+    (when content-type
+      (setf (he/type entry) content-type))
+    (when last-modified
+      (setf (he/mtime entry) (encode-time (parse-time-string last-modified))))
+    (when x-file-block-length
+      (setf (map-elt (he/etc entry) 'block-length)
+            (ignore-errors
+              (cl-parse-integer x-file-block-length))))
+    (when x-file-block-length-downloaded
+      (setf (map-elt (he/etc entry) 'block-length-downloaded)
+            (ignore-errors
+              (cl-parse-integer x-file-block-length-downloaded))))
+
+    ;; Redisplay entry.
+    (unless (h//entry-directory-p entry)
+      ;; There's currently never a reason to redisplay directory entries since
+      ;; they don't have block-length{,-downloaded} metadata.
+      (he//invalidate entry))))
 
 (defun h/gateway-needs-upgrade-p ()
   "Return non-nil if the gateway is responsive and needs upgraded."
@@ -708,24 +736,10 @@ the given `plz-queue'"
          :noquery t))))
 
 (defun he//fill (entry headers)
-  "Fill ENTRY slots from HEADERS.
-
-- \\+`type'
-- \\+`mtime'
-- \\+`size'
-
-Also fills existent range in `hyperdrive-version-ranges'.
+  "Fill existent range for ENTRY in `hyperdrive-version-ranges' from HEADERS.
 
 Returns filled ENTRY."
-  (pcase-let*
-      (((map content-length content-type etag last-modified) headers))
-    (when last-modified
-      (setf last-modified (encode-time (parse-time-string last-modified))))
-    (setf (he/size entry) (and content-length
-                               (ignore-errors
-                                 (cl-parse-integer content-length))))
-    (setf (he/type entry) content-type)
-    (setf (he/mtime entry) last-modified)
+  (pcase-let (((map etag) headers))
     (when (and etag (not (h//entry-directory-p entry)))
       ;; Directory version ranges are not supported.
       (h/update-existent-version-range entry (string-to-number etag)))
@@ -738,15 +752,21 @@ LISTING should be an alist based on the JSON retrieved 
in, e.g.,
 `hyperdrive-dir-handler'.  Fills existent version ranges for each
 entry as a side-effect."
   (mapcar
-   (pcase-lambda ((map seq key value))
+   (pcase-lambda ((map seq key value blockLengthDownloaded))
      (let* ((mtime (map-elt (map-elt value 'metadata) 'mtime))
             (size (map-elt (map-elt value 'blob) 'byteLength))
+            (block-length (map-elt (map-elt value 'blob) 'blockLength))
             (entry (he/create
                     :hyperdrive hyperdrive :path key :version version)))
        (when mtime ; mtime is milliseconds since epoch
          (setf (he/mtime entry) (seconds-to-time (/ mtime 1000.0))))
        (when size
          (setf (he/size entry) size))
+       (when block-length
+         (setf (map-elt (he/etc entry) 'block-length) block-length))
+       (when blockLengthDownloaded
+         (setf (map-elt (he/etc entry) 'block-length-downloaded)
+               blockLengthDownloaded))
        (when seq
          ;; seq is the hyperdrive version *before* the entry was added/modified
          (hyperdrive-update-existent-version-range entry (1+ seq)))
diff --git a/hyperdrive-vars.el b/hyperdrive-vars.el
index 8102ffdb74..fc2ede7c43 100644
--- a/hyperdrive-vars.el
+++ b/hyperdrive-vars.el
@@ -314,6 +314,15 @@ value (and should only be present once in the string).  
Used in
 (defface h/size '((t (:inherit font-lock-doc-face)))
   "File sizes.")
 
+(defface h/size-fully-downloaded '((t (:inherit success)))
+  "File sizes for entries which have been fully downloaded.")
+
+(defface h/size-not-downloaded '((t (:inherit error)))
+  "File sizes for entries which have not been downloaded.")
+
+(defface h/size-partially-downloaded '((t (:inherit warning)))
+  "File sizes for entries which have been partially downloaded.")
+
 (defface h/timestamp '((t (:inherit default)))
   "Entry timestamp.")
 
@@ -372,7 +381,7 @@ values are alists mapping version range starts to plists 
with
 ;;;;; Internals
 
 (defvar h/gateway-version-expected
-  '(:name "hyper-gateway-ushin" :version "3.10.2"))
+  '(:name "hyper-gateway-ushin" :version "3.11.0"))
 
 (defvar h/gateway-version-checked-p nil
   "Non-nil if the gateway's version has been checked.
diff --git a/hyperdrive.el b/hyperdrive.el
index a29d43727b..d5d98e665f 100644
--- a/hyperdrive.el
+++ b/hyperdrive.el
@@ -201,8 +201,8 @@ modified; file blobs may be recoverable from other peers."
       :headers '(("Cache-Control" . "no-store"))
       :else (lambda (err)
               (h/error "Unable to clear cache for `%s': %S" (he/url entry) 
err))
-      :then (lambda (_response)
-              (h/message "Cleared `%s'" (h//format-entry entry))))))
+      ;; Make async; `he//api-then' will call `he//invalidate'.
+      :then #'ignore)))
 
 ;;;###autoload
 (defun hyperdrive-purge (hyperdrive)
@@ -1384,24 +1384,21 @@ Intended for relative (i.e. non-full) URLs."
 ;;;;; Installation
 
 (defvar h/gateway-urls-and-hashes
-  ;; TODO: sr.ht build (<https://builds.sr.ht/~ushin/job/1247130#task-setup>)
-  ;; fail due to a kernel issue: https://github.com/nodejs/node/issues/53051
   '((gnu/linux
-     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.10.2/hyper-gateway-ushin-linux";
-       :sha256 
"4a79a406ab8e6f8f9c4e47bd7f001c6673c81e1357c3977e0dddfac777a57204")
-     ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.10.2/hyper-gateway-linux-v3.10.2";
-       :sha256 
"4a79a406ab8e6f8f9c4e47bd7f001c6673c81e1357c3977e0dddfac777a57204"))
+     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.11.0/hyper-gateway-ushin-linux";
+       :sha256 
"2074ec69c3e699105e132e774996c15ba3d9f14019f0cf5bc1bd15c35f7524c7")
+     ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.11.0/hyper-gateway-linux-v3.11.0";
+       :sha256 
"69d8ee0bc6442de9e57962bcf151febee6b93607907e846ba8ec5d2ad2605d38"))
     (darwin
-     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.10.2/hyper-gateway-ushin-macos";
-       :sha256 
"84ee621756dce98597f43b45942af812c9f3b200f31f769a53d077bf7e8b1156")
-     ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.10.2/hyper-gateway-macos-v3.10.2";
-       :sha256 
"84ee621756dce98597f43b45942af812c9f3b200f31f769a53d077bf7e8b1156"))
+     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.11.0/hyper-gateway-ushin-macos";
+       :sha256 
"219d673ea28dbc69f7cb5fbd5a01ed2b69f3f281f1e22f0d20e871c755eb02cf")
+     ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.11.0/hyper-gateway-macos-v3.11.0";
+       :sha256 
"c7dd08005037e6b27aafffe79d70255179a1c95a4699ba227d4b79f18badf38b"))
     (windows-nt
-     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.10.2/hyper-gateway-ushin-windows.exe";
-       :sha256 
"691105d1657627d2ac5e56b6b598ce3572beaa601663a55a86135b8cd1c6657d")
-     ;; ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.10.2/hyper-gateway-windows-v3.10.2.exe";
-     ;;   :sha256 "")
-     ))
+     ( :url 
"https://codeberg.org/USHIN/hyper-gateway-ushin/releases/download/v3.11.0/hyper-gateway-ushin-windows.exe";
+       :sha256 
"1e4e303766e1043355d27387e487e4296c03a3d6877d5920c6ae4c12d80f7bd4")
+     ( :url 
"https://git.sr.ht/~ushin/hyper-gateway-ushin/refs/download/v3.11.0/hyper-gateway-windows-v3.11.0.exe";
+       :sha256 
"69277b1748d16d274644151a1c7dbcaa7290689ae44f79ffba94a29f77978f4b")))
   "Alist mapping `system-type' to URLs where the gateway can be downloaded.")
 
 ;;;###autoload

Reply via email to