Преглед на файлове

Added wip properties sidebar

Toby Chui преди 2 години
родител
ревизия
1db8a212e9
променени са 5 файла, в които са добавени 318 реда и са изтрити 122 реда
  1. 1 1
      file_system.go
  2. 134 110
      mod/filesystem/fssort/fssort.go
  3. 2 5
      web/SystemAO/file_system/file_explorer.css
  4. 165 4
      web/SystemAO/file_system/file_explorer.html
  5. 16 2
      web/SystemAO/locale/file_explorer.json

+ 1 - 1
file_system.go

@@ -3110,7 +3110,7 @@ func system_fs_handleFolderSortModePreference(w http.ResponseWriter, r *http.Req
 			return
 			return
 		}
 		}
 
 
-		if !utils.StringInArray([]string{"default", "reverse", "smallToLarge", "largeToSmall", "mostRecent", "leastRecent"}, sortMode) {
+		if !utils.StringInArray(fssort.ValidSortModes, sortMode) {
 			utils.SendErrorResponse(w, "Not supported sort mode: "+sortMode)
 			utils.SendErrorResponse(w, "Not supported sort mode: "+sortMode)
 			return
 			return
 		}
 		}

+ 134 - 110
mod/filesystem/fssort/fssort.go

@@ -1,110 +1,134 @@
-package fssort
-
-import (
-	"io/fs"
-	"path/filepath"
-	"sort"
-	"strings"
-)
-
-type sortBufferedStructure struct {
-	Filename string
-	Filepath string
-	Filesize int64
-	ModTime  int64
-}
-
-/*
-	Quick utilties to sort file list according to different modes
-*/
-func SortFileList(filelistRealpath []string, fileInfos []fs.FileInfo, sortMode string) []string {
-	//Build a filelist with information based on the given filelist
-	parsedFilelist := []*sortBufferedStructure{}
-	if len(filelistRealpath) != len(fileInfos) {
-		//Invalid usage
-		return filelistRealpath
-	}
-	for i, file := range filelistRealpath {
-		thisFileInfo := sortBufferedStructure{
-			Filename: filepath.Base(file),
-			Filepath: file,
-		}
-
-		//Get Filesize
-		fi := fileInfos[i]
-		thisFileInfo.Filesize = fi.Size()
-		thisFileInfo.ModTime = fi.ModTime().Unix()
-
-		parsedFilelist = append(parsedFilelist, &thisFileInfo)
-
-	}
-
-	//Sort the filelist
-	if sortMode == "default" {
-		//Sort by name, convert filename to window sorting methods
-		sort.Slice(parsedFilelist, func(i, j int) bool {
-			return strings.ToLower(parsedFilelist[i].Filename) < strings.ToLower(parsedFilelist[j].Filename)
-		})
-	} else if sortMode == "reverse" {
-		//Sort by reverse name
-		sort.Slice(parsedFilelist, func(i, j int) bool {
-			return strings.ToLower(parsedFilelist[i].Filename) > strings.ToLower(parsedFilelist[j].Filename)
-		})
-	} else if sortMode == "smallToLarge" {
-		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize < parsedFilelist[j].Filesize })
-	} else if sortMode == "largeToSmall" {
-		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize > parsedFilelist[j].Filesize })
-	} else if sortMode == "mostRecent" {
-		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime > parsedFilelist[j].ModTime })
-	} else if sortMode == "leastRecent" {
-		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime < parsedFilelist[j].ModTime })
-	} else if sortMode == "smart" {
-		parsedFilelist = SortNaturalFilelist(parsedFilelist)
-	}
-
-	results := []string{}
-	for _, sortedFile := range parsedFilelist {
-		results = append(results, sortedFile.Filepath)
-	}
-
-	return results
-}
-
-func SortDirEntryList(dirEntries []fs.DirEntry, sortMode string) []fs.DirEntry {
-	entries := map[string]fs.DirEntry{}
-	fnames := []string{}
-	fis := []fs.FileInfo{}
-
-	for _, de := range dirEntries {
-		fnames = append(fnames, de.Name())
-		fstat, _ := de.Info()
-		fis = append(fis, fstat)
-		thisFsDirEntry := de
-		entries[de.Name()] = thisFsDirEntry
-	}
-
-	//Sort it
-	sortedNameList := SortFileList(fnames, fis, sortMode)
-
-	//Update dirEntry sequence
-	newDirEntry := []fs.DirEntry{}
-	for _, key := range sortedNameList {
-		newDirEntry = append(newDirEntry, entries[key])
-	}
-
-	return newDirEntry
-}
-
-func SortModeIsSupported(sortMode string) bool {
-	return contains(sortMode, []string{"default", "reverse", "smallToLarge", "largeToSmall", "mostRecent", "leastRecent", "smart"})
-}
-
-func contains(item string, slice []string) bool {
-	set := make(map[string]struct{}, len(slice))
-	for _, s := range slice {
-		set[s] = struct{}{}
-	}
-
-	_, ok := set[item]
-	return ok
-}
+package fssort
+
+import (
+	"io/fs"
+	"path/filepath"
+	"sort"
+	"strings"
+)
+
+type sortBufferedStructure struct {
+	Filename string
+	Filepath string
+	Filesize int64
+	ModTime  int64
+}
+
+var ValidSortModes = []string{"default", "reverse", "smallToLarge", "largeToSmall", "mostRecent", "leastRecent", "smart", "fileTypeAsce", "fileTypeDesc"}
+
+/*
+	Quick utilties to sort file list according to different modes
+*/
+func SortFileList(filelistRealpath []string, fileInfos []fs.FileInfo, sortMode string) []string {
+	//Build a filelist with information based on the given filelist
+	parsedFilelist := []*sortBufferedStructure{}
+	if len(filelistRealpath) != len(fileInfos) {
+		//Invalid usage
+		return filelistRealpath
+	}
+	for i, file := range filelistRealpath {
+		thisFileInfo := sortBufferedStructure{
+			Filename: filepath.Base(file),
+			Filepath: file,
+		}
+
+		//Get Filesize
+		fi := fileInfos[i]
+		thisFileInfo.Filesize = fi.Size()
+		thisFileInfo.ModTime = fi.ModTime().Unix()
+
+		parsedFilelist = append(parsedFilelist, &thisFileInfo)
+
+	}
+
+	//Sort the filelist
+	if sortMode == "default" {
+		//Sort by name, convert filename to window sorting methods
+		sort.Slice(parsedFilelist, func(i, j int) bool {
+			return strings.ToLower(parsedFilelist[i].Filename) < strings.ToLower(parsedFilelist[j].Filename)
+		})
+	} else if sortMode == "reverse" {
+		//Sort by reverse name
+		sort.Slice(parsedFilelist, func(i, j int) bool {
+			return strings.ToLower(parsedFilelist[i].Filename) > strings.ToLower(parsedFilelist[j].Filename)
+		})
+	} else if sortMode == "smallToLarge" {
+		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize < parsedFilelist[j].Filesize })
+	} else if sortMode == "largeToSmall" {
+		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].Filesize > parsedFilelist[j].Filesize })
+	} else if sortMode == "mostRecent" {
+		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime > parsedFilelist[j].ModTime })
+	} else if sortMode == "leastRecent" {
+		sort.Slice(parsedFilelist, func(i, j int) bool { return parsedFilelist[i].ModTime < parsedFilelist[j].ModTime })
+	} else if sortMode == "smart" {
+		parsedFilelist = SortNaturalFilelist(parsedFilelist)
+	} else if sortMode == "fileTypeAsce" {
+		sort.Slice(parsedFilelist, func(i, j int) bool {
+			exti := filepath.Ext(parsedFilelist[i].Filename)
+			extj := filepath.Ext(parsedFilelist[j].Filename)
+
+			exti = strings.TrimPrefix(exti, ".")
+			extj = strings.TrimPrefix(extj, ".")
+
+			return exti < extj
+
+		})
+	} else if sortMode == "fileTypeDesc" {
+		sort.Slice(parsedFilelist, func(i, j int) bool {
+			exti := filepath.Ext(parsedFilelist[i].Filename)
+			extj := filepath.Ext(parsedFilelist[j].Filename)
+
+			exti = strings.TrimPrefix(exti, ".")
+			extj = strings.TrimPrefix(extj, ".")
+
+			return exti > extj
+
+		})
+	}
+
+	results := []string{}
+	for _, sortedFile := range parsedFilelist {
+		results = append(results, sortedFile.Filepath)
+	}
+
+	return results
+}
+
+func SortDirEntryList(dirEntries []fs.DirEntry, sortMode string) []fs.DirEntry {
+	entries := map[string]fs.DirEntry{}
+	fnames := []string{}
+	fis := []fs.FileInfo{}
+
+	for _, de := range dirEntries {
+		fnames = append(fnames, de.Name())
+		fstat, _ := de.Info()
+		fis = append(fis, fstat)
+		thisFsDirEntry := de
+		entries[de.Name()] = thisFsDirEntry
+	}
+
+	//Sort it
+	sortedNameList := SortFileList(fnames, fis, sortMode)
+
+	//Update dirEntry sequence
+	newDirEntry := []fs.DirEntry{}
+	for _, key := range sortedNameList {
+		newDirEntry = append(newDirEntry, entries[key])
+	}
+
+	return newDirEntry
+}
+
+func SortModeIsSupported(sortMode string) bool {
+	return contains(sortMode, []string{"default", "reverse", "smallToLarge", "largeToSmall", "mostRecent", "leastRecent", "smart"})
+}
+
+func contains(item string, slice []string) bool {
+	set := make(map[string]struct{}, len(slice))
+	for _, s := range slice {
+		set[s] = struct{}{}
+	}
+
+	_, ok := set[item]
+	return ok
+}

+ 2 - 5
web/SystemAO/file_system/file_explorer.css

@@ -488,13 +488,10 @@ body{
     width: 300px;
     width: 300px;
     min-width: 300px;
     min-width: 300px;
     padding: 1em;
     padding: 1em;
+    overflow-y: auto;
+    scrollbar-width: thin;
 }
 }
 
 
-#propertiesView iframe{
-    height: 100%;
-}
-
-
 /*
 /*
     Context Menu
     Context Menu
 
 

+ 165 - 4
web/SystemAO/file_system/file_explorer.html

@@ -62,6 +62,9 @@
                     <option value="largeToSmall" locale="menu/sort/large"> Largest</option>
                     <option value="largeToSmall" locale="menu/sort/large"> Largest</option>
                     <option value="mostRecent" locale="menu/sort/mostrecent">Most Recent</option>
                     <option value="mostRecent" locale="menu/sort/mostrecent">Most Recent</option>
                     <option value="leastRecent" locale="menu/sort/leastrecent">Least Recent</option>
                     <option value="leastRecent" locale="menu/sort/leastrecent">Least Recent</option>
+                    <option value="fileTypeAsce" locale="menu/sort/typeAsc">File Type (Asce)</option>
+                    <option value="fileTypeDesc" locale="menu/sort/typeDes">File Type (Desc)</option>
+                    <option value="smart" locale="menu/sort/smart">Smart</option>
                 </select>
                 </select>
                <div class="buttons">
                <div class="buttons">
                     <!-- Viewport-->
                     <!-- Viewport-->
@@ -144,9 +147,25 @@
                 </div>
                 </div>
                 <br>
                 <br>
             </div>
             </div>
-            <div id="propertiesView">
-                <h1>WORK IN PROGRESS</h1>
-                <h3>PREVIEW WINDOW</h3>
+            <div id="propertiesView" class="small">
+                <div class="preview" style="margin-top: 0.4em;">
+                    <img class="ui fluid image" >
+                </div>
+                <h3 class="ui header" style="margin-top: 0.4em;">
+                    <span class="filename"></span>
+                    <div class="sub header vpath"></div>
+                </h3>
+                <table class="ui very basic table">
+                    <tbody class="propertiesTable">
+                      
+                    </tbody>
+                </table>
+                <div style="height: 34px;">
+                    <div class="ui right floated buttons">
+                        <button title="Share File" class="ui basic icon button shareBtn" onclick="shareFile();"><i class="share alternate icon"></i></button>
+                        <button title="Expand Properties Sidebar" class="ui basic icon button sizeToggle" onclick="togglePreviewWindowSize();"><i class="expand icon"></i></button>
+                    </div>
+                </div>
             </div>
             </div>
         </div>
         </div>
         <!-- Other popup windows -->
         <!-- Other popup windows -->
@@ -1551,6 +1570,10 @@
                         $(".fileObject.selected").removeClass("selected");
                         $(".fileObject.selected").removeClass("selected");
                         $(this).addClass("selected");
                         $(this).addClass("selected");
                         lastClickedFileID = $(this).attr("fileID");
                         lastClickedFileID = $(this).attr("fileID");
+
+                        if (propertiesView){
+                            loadFileProperties($(this).attr("filepath"));
+                        }
                     }
                     }
 
 
                     updateSelectedObjectsCount();
                     updateSelectedObjectsCount();
@@ -3663,8 +3686,9 @@
                         });
                         });
                     }
                     }
                     $("#folderView").css("height",windowHeight + "px");
                     $("#folderView").css("height",windowHeight + "px");
-                    
                 }
                 }
+
+                $("#propertiesView").css("height", windowHeight + "px");
             }
             }
 
 
             function toggleDarkTheme(){
             function toggleDarkTheme(){
@@ -5068,6 +5092,143 @@
             });
             });
         }
         }
 
 
+        /*
+            Properties Sidebar
+        */
+
+        function loadFileProperties(filepath){
+            $.ajax({
+                url: "../../system/file_system/getProperties",
+                method: "POST",
+                data: {path: filepath},
+                success: function(data){
+                    if (data.error !== undefined){
+                        //Failed to load 
+
+                    }else{
+                        let previewSidebar = $("#propertiesView");
+                        //Check the extension. If the extension is image supported 
+                        //by web browser, enable full resolution button
+                        let enableFullResButton = false;
+                        if (!data.IsDirectory && data.Basename.indexOf(".") >= 0){
+                            let ext = data.Basename.split(".").pop().toLowerCase();
+                            if (ext == "jpeg" || ext == "jpg" || ext == "webp" || ext == "png" || ext == "gif"){
+                               //Web preview-able formats
+
+                            }
+                        }
+                        
+                        //Load the preview 
+                        fetch("../../system/file_system/loadThumbnail?vpath=" + encodeURIComponent(data.VirtualPath))
+                        .then((response) => response.json())
+                        .then((imageData) => {
+                            if (imageData.error !== undefined || imageData.trim() == ""){
+                                //Image load error.
+                                if (data.IsDirectory){
+                                    //Load the folder image
+                                    $(previewSidebar).find(".preview img").attr("src", "../../img/system/folder.svg")
+                                }else{
+                                    let icon = "file outline";
+                                    let ext = data.Basename.split(".").pop().toLowerCase();
+                                    if (ext != ""){
+                                        icon = ao_module_utils.getIconFromExt(ext);
+                                    }
+                                    let imagePath = "../../img/desktop/files_icon/default/" + icon + ".png";
+                                    $(previewSidebar).find(".preview img").attr("src", imagePath);
+                                }
+                               
+                                return;
+                            }
+                            $(previewSidebar).find(".preview img").attr('src',"data:image/jpg;base64," + imageData);
+                        });
+
+                        //Render the remaining information
+                        $("#propertiesView").find(".filename").text(data.Basename);
+                        $("#propertiesView").find(".vpath").text(data.VirtualPath);
+
+                        let propTable = $("#propertiesView").find(".propertiesTable");
+                        $(propTable).html("");
+                        $(propTable).append(`<tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/filesize", "File Size")}
+                            </td>
+                            <td>
+                                ${bytesToSize(data.Filesize)}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/modtime", "Last Modification")}
+                            </td>
+                            <td>
+                                ${data.LastModTime}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/mimetype", "MIME Type")}
+                            </td>
+                            <td>
+                                ${data.MimeType}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/owner", "Owner")}
+                            </td>
+                            <td>
+                                ${data.Owner}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/permission", "Permission")}
+                            </td>
+                            <td>
+                                ${data.Permission}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/storepath", "Storage Path")}
+                            </td>
+                            <td>
+                                ${data.StoragePath}
+                            </td>
+                        </tr><tr>
+                            <td>
+                                ${applocale.getString("sidebar/properties/vpath", "Virtualized Path")}
+                            </td>
+                            <td>
+                                ${data.VirtualPath}
+                            </td>
+                        </tr>`);
+
+                    }
+                    console.log(data);
+                }
+            })
+        }
+
+        //Toggle preview windows size from small to large mode,
+        //set restoreDefault to true for force small interface
+        function togglePreviewWindowSize(restoreDefault = false){
+            if ($("#propertiesView").hasClass("small") && restoreDefault == false){
+                //Set it to big
+                $("#propertiesView").css({
+                    "width": "500px",
+                    "min-width": "500px"
+                });
+                $("#propertiesView").removeClass("small").addClass("big");
+                $("#propertiesView").find(".sizeToggle").html(`<i class="compress icon"></i>`);
+                $("#propertiesView").find(".sizeToggle").attr("title", applocale.getString("sidebar/properties/shrink", "Shrink Properties Sidebar"));
+            }else{
+                //Set it to small
+                $("#propertiesView").css({
+                    "width": "300px",
+                    "min-width": "300px"
+                });
+                $("#propertiesView").removeClass("big").addClass("small");
+                $("#propertiesView").find(".sizeToggle").html(`<i class="expand icon"></i>`);
+                $("#propertiesView").find(".sizeToggle").attr("title", applocale.getString("sidebar/properties/expand", "Expand Properties Sidebar"));
+            }
+        }
+
         /*
         /*
             Integration functions
             Integration functions
         */
         */

+ 16 - 2
web/SystemAO/locale/file_explorer.json

@@ -14,6 +14,9 @@
                 "menu/sort/large": "大至小",
                 "menu/sort/large": "大至小",
                 "menu/sort/mostrecent": "新到舊",
                 "menu/sort/mostrecent": "新到舊",
                 "menu/sort/leastrecent": "舊到新",
                 "menu/sort/leastrecent": "舊到新",
+                "menu/sort/smart": "自然數字順序",
+                "menu/sort/typeAsc": "副檔名順序",
+                "menu/sort/typeDes": "副檔名倒序",
 
 
                 "fileopr/Back": "上一頁",
                 "fileopr/Back": "上一頁",
                 "fileopr/Parent Folder": "返回上層目錄",
                 "fileopr/Parent Folder": "返回上層目錄",
@@ -54,6 +57,16 @@
                 "tooltip/filecount":" 個項目",
                 "tooltip/filecount":" 個項目",
                 "tooltip/selectedcount":" 個已選",
                 "tooltip/selectedcount":" 個已選",
 
 
+                "sidebar/properties/expand": "放大資訊側欄",
+                "sidebar/properties/shrink":"縮小資訊側欄",
+                "sidebar/properties/filesize":"檔案大小",
+                "sidebar/properties/modtime":"最後更改日期",
+                "sidebar/properties/mimetype":"媒體類型",
+                "sidebar/properties/owner":"擁有者",
+                "sidebar/properties/permission":"存取權限",
+                "sidebar/properties/storepath":"儲存位置",
+                "sidebar/properties/vpath":"虛擬化位置",
+                
 
 
                 "sidebar/vroot/user": "使用者",
                 "sidebar/vroot/user": "使用者",
                 "sidebar/vroot/fsh": "虛擬儲存裝置",
                 "sidebar/vroot/fsh": "虛擬儲存裝置",
@@ -252,8 +265,9 @@
                 "List View": "顯示檔案列表",
                 "List View": "顯示檔案列表",
                 "Detail View": "顯示詳細資料",
                 "Detail View": "顯示詳細資料",
                 "Case Sensitive": "區分大小寫",
                 "Case Sensitive": "區分大小寫",
-                "Clear Search": "清除搜尋結果"
-                
+                "Clear Search": "清除搜尋結果",
+                "Expand Properties Sidebar":"放大資訊側欄",
+                "Shrink Properties Sidebar":"縮小資訊側欄"
             },
             },
             "placeholder": {
             "placeholder": {
                 "New Filename": "在此輸入新檔案名稱",
                 "New Filename": "在此輸入新檔案名稱",