implement cd on double click and show stats on single click

This commit is contained in:
joshuaboud 2021-05-25 13:47:23 -03:00
parent da08328386
commit 0d12a2d4b9
No known key found for this signature in database
GPG Key ID: 17EFB59E2A8BF50E
3 changed files with 110 additions and 27 deletions

View File

@ -43,7 +43,7 @@
<div class="flex-row inner-container"> <div class="flex-row inner-container">
<div class="contents-view" id="nav-contents-view"></div> <div class="contents-view" id="nav-contents-view"></div>
<div class="horizontal-spacer"></div> <div class="horizontal-spacer"></div>
<div class="info-column"></div> <div class="info-column" id="nav-info-column"></div>
</div> </div>
</div> </div>
</body> </body>

View File

@ -1,7 +1,7 @@
class NavEntry { class NavEntry {
constructor(/*string or array*/ path) { constructor(/*string or array*/ path, /*dict*/ stat) {
if(typeof path == 'string') if(typeof path == 'string')
this.path = path.split('/').splice(1); this.path = path.split('/').splice(1);
else else
@ -14,6 +14,8 @@ class NavEntry {
title.innerText = this.filename(); title.innerText = this.filename();
this.dom_element.appendChild(icon); this.dom_element.appendChild(icon);
this.dom_element.appendChild(title); this.dom_element.appendChild(title);
this.stat = stat;
this.dom_element.nav_item_icon.addEventListener("click", this)
} }
destroy() { destroy() {
while(this.dom_element.firstChild){ while(this.dom_element.firstChild){
@ -23,7 +25,10 @@ class NavEntry {
this.dom_element.parentElement.removeChild(this.dom_element); this.dom_element.parentElement.removeChild(this.dom_element);
} }
filename() { filename() {
return this.path[this.path.length -1]; var name = this.path[this.path.length -1];
if(name === "")
name = "/";
return name;
} }
path_str() { path_str() {
return "/" + this.path.join('/'); return "/" + this.path.join('/');
@ -31,47 +36,81 @@ class NavEntry {
show() { show() {
document.getElementById("nav-contents-view").appendChild(this.dom_element); document.getElementById("nav-contents-view").appendChild(this.dom_element);
} }
} get_properties() {
return this.stat;
class NavFile extends NavEntry { }
constructor(/*string or array*/ path) { show_properties(){
super(path); var html = '<div class="nav-info-column-filename">' + this.filename() + '</div>\n';
this.nav_type = "file"; html += '<div class="nav-property-pair">';
this.dom_element.nav_item_icon.classList.add("nav-file-icon"); html += '<span class="nav-property-pair-key">Mode: </span>'
html += '<span class="nav-property-pair-value">' + this.stat["mode-str"] + '</span>'
html += '</div>'
document.getElementById("nav-info-column").innerHTML = html;
} }
} }
class NavDir extends NavEntry { class NavFile extends NavEntry {
constructor(/*string or array*/ path, nav_window_ref) { constructor(/*string or array*/ path, /*dict*/ stat) {
super(path); super(path, stat);
this.nav_type = "dir"; this.nav_type = "file";
this.dom_element.nav_item_icon.classList.add("nav-dir-icon"); this.dom_element.nav_item_icon.classList.add("nav-file-icon");
this.nav_window_ref = nav_window_ref;
this.dom_element.nav_item_icon.addEventListener("click", this)
} }
handleEvent(e) { handleEvent(e) {
switch(e.type){ switch(e.type){
case "click": case "click":
this.nav_window_ref.cd(this); this.show_properties();
e.stopPropagation();
break;
}
}
}
class NavDir extends NavEntry {
constructor(/*string or array*/ path, /*dict*/ stat, nav_window_ref) {
super(path, stat);
this.nav_type = "dir";
this.dom_element.nav_item_icon.classList.add("nav-dir-icon");
this.nav_window_ref = nav_window_ref;
this.double_click = false;
}
handleEvent(e) {
switch(e.type){
case "click":
if(this.double_click)
this.nav_window_ref.cd(this);
else{ // single click
this.show_properties();
this.double_click = true;
if(this.timeout)
clearTimeout(this.timeout)
this.timeout = setTimeout(() => {
this.double_click = false;
}, 500);
}
e.stopPropagation();
break; break;
} }
} }
async get_children(nav_window_ref) { async get_children(nav_window_ref) {
var children = []; var children = [];
var data = await cockpit.spawn(["/usr/share/cockpit/navigator/scripts/ls.py", this.path_str()], {err:"ignore"}); var data = await cockpit.spawn(["/usr/share/cockpit/navigator/scripts/ls.py", this.path_str()], {err:"ignore"});
var entries = JSON.parse(data); var response = JSON.parse(data);
this.stat = response["."]["stat"];
var entries = response["children"];
entries.forEach(entry => { entries.forEach(entry => {
var filename = entry["filename"]; var filename = entry["filename"];
var path = (this.path.length >= 1 && this.path[0]) ? [... this.path, filename] : [filename]; var path = (this.path.length >= 1 && this.path[0]) ? [... this.path, filename] : [filename];
var stat = entry["stat"];
if(entry["isdir"]) if(entry["isdir"])
children.push(new NavDir(path, nav_window_ref)); children.push(new NavDir(path, stat, nav_window_ref));
else else
children.push(new NavFile(path)); children.push(new NavFile(path, stat));
}); });
children.sort((first, second) => { children.sort((first, second) => {
if(first.nav_type === second.nav_type) if(first.nav_type === second.nav_type){
return 0; return first.filename().localeCompare(second.filename());
if(first.nav_type == "dir") }
if(first.nav_type === "dir")
return -1; return -1;
return 1; return 1;
}) })
@ -83,6 +122,15 @@ class NavWindow {
constructor() { constructor() {
this.path_stack = [new NavDir("/", this)]; this.path_stack = [new NavDir("/", this)];
this.entries = []; this.entries = [];
this.window = document.getElementById("nav-contents-view");
this.window.addEventListener("click", this);
}
handleEvent(e) {
switch(e.type){
case "click":
this.show_pwd_properties();
break;
}
} }
async refresh() { async refresh() {
var files = await this.pwd().get_children(this); var files = await this.pwd().get_children(this);
@ -112,6 +160,9 @@ class NavWindow {
this.path_stack.pop(); this.path_stack.pop();
this.refresh(); this.refresh();
} }
show_pwd_properties() {
this.pwd().show_properties();
}
} }
let nav_window = new NavWindow(); let nav_window = new NavWindow();

View File

@ -1,8 +1,36 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os import os
from stat import S_ISDIR, filemode
import json import json
import sys import sys
from pwd import getpwuid
from grp import getgrgid
def get_stat(full_path, filename = '/'):
stats = os.lstat(full_path)
isdir = False
try:
isdir = S_ISDIR(os.stat(full_path).st_mode)
except OSError:
pass
response = {
"filename": filename,
"isdir": isdir,
"stat": {
"mode": stats.st_mode,
"mode-str": filemode(stats.st_mode),
"uid": stats.st_uid,
"owner": getpwuid(stats.st_uid).pw_name,
"gid": stats.st_gid,
"group": getgrgid(stats.st_gid).gr_name,
"size": stats.st_size,
"atime": stats.st_atime,
"mtime": stats.st_mtime,
"ctime": stats.st_ctime
}
}
return response
def main(): def main():
if(len(sys.argv) < 2): if(len(sys.argv) < 2):
@ -12,10 +40,14 @@ def main():
except: except:
print("No such file or directory") print("No such file or directory")
sys.exit(1) sys.exit(1)
response = [] response = {
".": get_stat(sys.argv[1]),
"children": []
}
for node in nodes: for node in nodes:
response.append({"filename": node, "isdir": os.path.isdir(sys.argv[1] + "/" + node)}) full_path = sys.argv[1] + "/" + node
print(json.dumps(response, indent=4)) response["children"].append(get_stat(full_path, node))
print(json.dumps(response))
if __name__ == "__main__": if __name__ == "__main__":