Browse Source

Keep current application state in URL hash

This commit makes it so that all application state is first written
to the URL hash and then evaluated from there. This has the
benefit that the URL hash is the ground truth for the application
state and can therefore be copied and sent to others.
2xB 8 months ago
parent
commit
808f96d8c5
1 changed files with 144 additions and 77 deletions
  1. 144 77
      viewer/archive/archiveviewer.js

+ 144 - 77
viewer/archive/archiveviewer.js

@@ -38,7 +38,6 @@ function hide_iframes(text) {
 }
 
 function date_to_iso(value) {
-    console.log(value)
     if ((value + "").includes("-")) {
         return value
     }
@@ -58,6 +57,12 @@ class ArchiveView {
         this.url = url
         this.project_id = project_id
         this.branch = branch
+
+        this.next_commit = ""
+        this.current_commit = ""
+
+        this.last_from = ""
+        this.last_to = ""
         
         this.maindiv = maindiv
         
@@ -79,7 +84,7 @@ class ArchiveView {
         option_range.innerHTML = "Range"
         this.selecttype.appendChild(option_range)
         
-        this.selecttype.addEventListener("input",(() => this.on_selecttype_selected()))
+        this.selecttype.addEventListener("input",(() => this.update_hash()))
         
         // ---
         
@@ -93,7 +98,7 @@ class ArchiveView {
         var maxdate = new Date().toLocaleDateString('en-ca')
         this.dateselector.value = ""
         this.dateselector.max = maxdate
-        this.dateselector.addEventListener("input",((event) => this.on_date_selected(event)))
+        this.dateselector.addEventListener("input",(() => this.update_hash()))
         
         outer_span.appendChild(this.dateselector)
         
@@ -109,7 +114,7 @@ class ArchiveView {
         this.range_from.value = ""
         this.range_from.min = mindate + "T00:00"
         this.range_from.max = maxdate + "T23:59"
-        this.range_from.addEventListener("input",(() => this.on_range_from_selected()))
+        this.range_from.addEventListener("input",(() => this.update_hash()))
         
         this.rangespan.appendChild(this.range_from)
         
@@ -123,7 +128,7 @@ class ArchiveView {
         
         this.range_to.value = ""
         this.range_to.max = maxdate + "T23:59"
-        this.range_to.addEventListener("input",(() => this.on_range_to_selected()))
+        this.range_to.addEventListener("input",(() => this.update_hash()))
 
         this.rangespan.appendChild(this.range_to)
         
@@ -152,7 +157,7 @@ class ArchiveView {
         
         this.index = index
         
-        const blobContent = new Blob([`
+        this.defaultcontent = new Blob([`
         <!doctype html><html><head>
         <style>
         body {
@@ -177,69 +182,149 @@ class ArchiveView {
             </body>
         </html>
         `], {type: "text/html"})
-        this.content.src = URL.createObjectURL(blobContent)
+        this.content.src = URL.createObjectURL(this.defaultcontent)
         
         window.addEventListener('hashchange', (event) => this.on_hash_changed(event))
         
         // Updates
         this.on_hash_changed(null)
-        this.on_selecttype_selected() // Update visibilities
+        this.update_hash() // Update visibilities
         this.load_pages() // Update version list
     }
+
+    update_hash() {
+        var old_hash = window.location.hash
+        var new_hash = ""
+
+        if (this.selecttype.value == "date") {
+            new_hash = "#date=" + this.dateselector.value + "&commit=" + this.next_commit
+        }
+        else {
+            new_hash = "#from=" + this.range_from.value + "&to=" + this.range_to.value + "&commit=" + this.next_commit
+        }
+
+        if (new_hash == old_hash) {
+            return false
+        }
+
+        window.location.hash = new_hash
+        return true
+    }
     
     on_hash_changed(event) {
         var remaining_hash = window.location.hash.substring(1).split("&")
         
         var date_from = null
         var length = null
-        var to = null
-        
-        var provided_full_range = false;
+        var date_to = null
+        var date = null
+
         while (remaining_hash.length > 0) {
             var current_part = remaining_hash.pop()
             
             var [key, value] = current_part.split("=")
             
             if (key == "from") {
-                date_from = value
+                date_from = ""
+
+                if (value) {
+                    date_from = date_to_iso(value)
+                }
             }
             else if (key == "length") {
                 length = value
             }
             else if (key == "to") {
-                to = value
+                date_to = ""
+
+                if (value) {
+                    date_to = date_to_iso(value)
+                }
+            }
+            else if (key == "date"){
+                date = ""
+
+                if (value) {
+                    date = date_to_iso(value)
+                }
+            }
+            else if (key == "commit"){
+                this.next_commit = value
             }
         }
-        
-        if (date_from != null) {
-            this.selecttype.value = "range"
-            var from_iso = date_to_iso(date_from)
-            console.log(date_from, from_iso)
-            this.range_from.value = from_iso
-            this.on_range_from_selected()
+
+        if (date) {
+            this.dateselector.value = date
             
-            if (length != null) {
-                // Only works when given UNIX timestamps
-                console.log(from_iso)
-                console.log(from_iso,date_to_iso(date_to_unix(from_iso)))
-                this.range_to.value = date_to_iso(date_to_unix(from_iso)+parseInt(length))
-                provided_full_range = true
+            this.selecttype.value = "date"
+
+            // Convert date to range so one only needs to work with ranges
+            if (this.dateselector.value) {
+                var date_from_tmp = this.dateselector.value
+                date_from = date_from_tmp + "T00:00"
+                
+                var date_to_obj = new Date(date_from_tmp)
+                date_to_obj.setDate(date_to_obj.getDate() + 1)
+                // Works also across months, years
+                date_to = date_to_obj.toJSON().slice(0, 10) + "T00:00"
             }
         }
-        
-        if (to != null) {
-            if (date_from != nullptr) {
-                provided_full_range = true
-            }
+
+        if (date_from) {
+            this.range_from.value = date_from
             
-            this.selecttype.value = "range"
-            this.on_selecttype_selected() // Update visibilities
-            this.range_to.value = date_to_iso(to)
+            this.range_to.min = date_from
+            this.range_to.disabled = (date_from == "")
+
+            if (!date) {
+                this.selecttype.value = "range"
+            }
+        }
+
+        if (date_to) {
+            this.range_to.value = date_to
+        }
+
+        if (length) {
+            this.range_to.value = date_to_iso(date_to_unix(date_from)+parseInt(length))
+        }
+
+        if (this.update_hash()) {
+            return // Triggered another hash change
+        }
+        
+        // Visibilities
+        var is_date = this.selecttype.value == "date"
+        this.dateselector.hidden = !is_date
+        this.rangespan.hidden = is_date
+
+        if (!is_date) {
+            this.dateselector.value = ""
+        }
+
+        if (!date_from) {
+            return
+        }
+
+        if (!date_to) {
+            return
         }
+
+        if (this.commitsSelect.length && date_from == this.last_from && date_to == this.last_to) {
+            this.commitsSelect.value = this.next_commit
+            this.on_commit_selected()
+            return // List didn't change
+        }
+
+        this.last_from = date_from
+        this.last_to = date_to
         
-        if (provided_full_range) {
-            this.load_pages(true)
+        if (this.next_commit) {
+            this.load_pages(false)
+            return
         }
+        
+        this.load_pages(true)
     }
     
     add_commits(commits, open_first) {
@@ -258,47 +343,25 @@ class ArchiveView {
         })
     }
     
-    on_selecttype_selected() {
-        this.dateselector.value = ""
-        
-        var is_date = this.selecttype.value == "date"
-        
-        this.dateselector.hidden = !is_date
-        this.rangespan.hidden = is_date
-    }
-    
-    on_date_selected(event) {
-        if (!event.target.value)
-            return
-        var date_from = event.target.value
-        
-        var date_to_obj = new Date(date_from)
-        date_to_obj.setDate(date_to_obj.getDate() + 1)
-          // Works also across months, years
-        var date_to = date_to_obj.toJSON().slice(0, 10);
-        
-        this.range_from.value = date_from + "T00:00"
-        this.on_range_from_selected()
-        this.range_to.value = date_to + "T00:00"
-        this.on_range_to_selected()
-    }
-    
-    on_range_from_selected() {
-        var val = this.range_from.value
-        this.range_to.min = val
-        this.range_to.disabled = (val == "")
-        
-        this.load_pages()
-    }
-    
-    on_range_to_selected() {
-        this.load_pages()
-    }
-    
     on_commit_selected() {
-        var sha = this.commitsSelect.value
+        if (!this.commitsSelect.value) {
+            this.content.contentWindow.location.replace(URL.createObjectURL(this.defaultcontent))
+            this.next_commit = ""
+            this.update_hash()
+            return
+        }
+
+        this.next_commit = this.commitsSelect.value
+        this.update_hash()
+
+        if (this.next_commit == this.current_commit) {
+            return // Nothing changed
+        }
+
+        this.current_commit = this.next_commit
+
         var url_before = 'https://' + this.url + '/api/v4/projects/' + this.project_id + '/repository/files/' + encodeURIComponent(this.page_path + "/")
-        var url_after = '/raw?ref=' + sha + '&private_token=' + this.token
+        var url_after = '/raw?ref=' + this.next_commit + '&private_token=' + this.token
         var url = url_before + encodeURIComponent(this.index) + url_after
         fetch(url)
         .then(response => response.text())
@@ -306,9 +369,8 @@ class ArchiveView {
             var cleaned_text = hide_iframes(replace_urls(text, url_before, url_after))
             
             const blobContent = new Blob([cleaned_text], {type: "text/html"})
-            this.content.src = URL.createObjectURL(blobContent)
+            this.content.contentWindow.location.replace(URL.createObjectURL(blobContent)) // Don't affect browser history by content change (as opposed to this.content.src = ...)
         })
-
     }
     
     load_pages(open_first=false) {
@@ -356,6 +418,11 @@ class ArchiveView {
             if (length > 0) {
                 this.load_pages_recursive(page+1, date_from, date_to, false)
             }
+            else {
+                // Loading done, update commit
+                this.commitsSelect.value = this.next_commit
+                this.on_commit_selected()
+            }
         })
     }
 }