Nice little directory browser :D
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at master 218 lines 6.2 kB view raw
1@* 2 This file is part of Utatane. 3 4 Utatane is free software: you can redistribute it and/or modify it under 5 the terms of the GNU Affero General Public License as published by the Free 6 Software Foundation, either version 3 of the License, or (at your option) 7 any later version. 8 9 Utatane is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 11 FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for 12 more details. 13 14 You should have received a copy of the GNU Affero General Public License 15 along with Utatane. If not, see <http://www.gnu.org/licenses/>. 16*@ 17 18@page "/api/files" 19@using System.Diagnostics.CodeAnalysis 20@layout EmptyLayout 21 22<AppTitle Partial>@( Path == "/" ? "/" : Path.Split('/').Last() )</AppTitle> 23 24<input autocomplete="off" type="text" name="path" value="@Path" id="prop-path" hx-swap-oob="true" /> 25 26<Breadcrumbs Path="@Path" hx-swap-oob="true"/> 27 28<GoBackButton Path="@Path" hx-swap-oob="true" /> 29 30<table> 31<tbody> 32@foreach (var row in _rows) { 33 <tr class="fsobject @row.TypeString"> 34 @*<td> 35 @if (row.IsFile && !row.IsBad) { 36 <a 37 download 38 class="file-dl clickable" 39 href="@row.Href" 40 aria-label="Download file"></a> 41 } 42 </td>*@ 43 <td class="file-icon">@row.Icon</td> 44 <td class="file-name" data-order="@row.Name"> 45 @if (row.IsBad) { 46 <s class="text-stone-500"> 47 @(row.Name + row.Trail) 48 </s> 49 } else { 50 @if (row.IsFile) { 51 <a class="@(row.IsDotFile ? "dotfile" : null)" 52 href="@(row.Href + row.Trail)" 53 > 54 @row.Name 55 </a> 56 } else { 57 <a class="@(row.IsDotFile ? "dotfile" : null)" 58 href="@(row.Href + row.Trail)" 59 hx-get="/api/files?path=@row.Href" 60 hx-target="#filetable tbody" 61 hx-swap="innerHtml scroll:top" 62 hx-push-url="@(row.Href + row.Trail)" 63 hx-indicator="#filetable-body, #name-spinner, #filetable-head-time" 64 > 65 @row.Name<span class="dir-slash">/</span> 66 </a> 67 } 68 } 69 </td> 70 <td class="file-size" data-order="@row.Size"> 71 @if (row.IsFile) { 72 @if (row.IsBad) { 73 <s>@Utils.FormatFileSize(row.Size)</s> 74 } else { 75 @Utils.FormatFileSize(row.Size) 76 ; 77 } 78 } 79 </td> 80 <td class="file-date" data-order="@row.TimeUnix"> 81 @if (row.IsBad) { 82 <s><time>@row.TimeFmtDate @row.TimeFmtTime</time></s> 83 } else { 84 <time>@row.TimeFmtDate @row.TimeFmtTime</time> 85 } 86 </td> 87 </tr> 88} 89@* </tbody> *@ 90</tbody> 91</table> 92 93@code { 94 95 [SupplyParameterFromQuery(Name = "path")] 96 public required String? Path { get; set; } 97 98 [SupplyParameterFromQuery(Name = "sort-by")] 99 public required String? SortByColumn { get; set; } 100 101 [SupplyParameterFromQuery(Name = "sort-asc")] 102 public required String SortAsc { get; set; } 103 104 private DirectoryInfo _realPath; 105 private SortByColumns _sortByColumn; 106 private SortByDirection _sortByDirection; 107 private IOrderedEnumerable<FileRow> _rows; 108 109 protected override Task OnInitializedAsync() { 110 if (String.IsNullOrEmpty(Path)) 111 Path = "/"; 112 else if (Path.EndsWith("/") && Path != "/") 113 Path = Path.TrimEnd('/'); 114 115 // prechecked in middleware! 116 _realPath = Utils.VerifyPath(Path).Value.AsT1(); 117 _sortByColumn = SortByColumn switch { 118 "name" => SortByColumns.Name, 119 "size" => SortByColumns.Size, 120 "time" => SortByColumns.Time, 121 _ => SortByColumns.Name 122 }; 123 _sortByDirection = SortAsc switch { 124 "on" => SortByDirection.Ascending, 125 _ => SortByDirection.Descending 126 }; 127 128 _rows = _realPath.EnumerateFileSystemInfos() 129 .Select(fsi => new FileRow(fsi, Path ?? "/")) 130 .OrderByDescending(x => !x.IsFile); 131 132 if (_sortByDirection == SortByDirection.Ascending) { 133 _rows = _sortByColumn switch { 134 SortByColumns.Name => _rows.ThenBy(x => x.Name), 135 SortByColumns.Size => _rows.ThenBy(x => x.Size), 136 SortByColumns.Time => _rows.ThenBy(x => x.TimeUnix) 137 }; 138 139 if (_sortByColumn != SortByColumns.Name) 140 _rows = _rows.ThenBy(x => x.Name); 141 } else { 142 _rows = _sortByColumn switch { 143 SortByColumns.Name => _rows.ThenByDescending(x => x.Name), 144 SortByColumns.Size => _rows.ThenByDescending(x => x.Size), 145 SortByColumns.Time => _rows.ThenByDescending(x => x.TimeUnix) 146 }; 147 148 if (_sortByColumn != SortByColumns.Name) 149 _rows = _rows.ThenByDescending(x => x.Name); 150 } 151 152 return base.OnInitializedAsync(); 153 } 154 155 public enum SortByColumns { 156 Name, 157 Size, 158 Time, 159 } 160 161 public enum SortByDirection { 162 Ascending, 163 Descending, 164 } 165 166 public class FileRow { 167 public required FileSystemInfo Fsi { get; init; } 168 public required bool IsFile { get; init; } 169 public required bool IsBad { get; init; } 170 public required String TypeString { get; init; } 171 public required MarkupString Icon { get; init; } 172 public required String Name { get; init; } 173 public required bool IsDotFile { get; init; } 174 public required long Size { get; init; } 175 public required String TimeFmtTime { get; init; } 176 public required String TimeFmtDate { get; init; } 177 public required String TimeUnix { get; init; } 178 public required String? Href { get; init; } 179 public required char? Trail { get; init; } 180 181 [SetsRequiredMembers] 182 public FileRow(FileSystemInfo baseFsi, String currentPath) { 183 FileSystemInfo? _fsi; 184 bool _isBad = false; 185 186 try { 187 _fsi = baseFsi.UnravelLink(); 188 } catch { 189 _isBad = true; 190 _fsi = baseFsi; 191 } 192 193 DateTime _dt = baseFsi.LastWriteTime.ToUniversalTime(); 194 195 Fsi = _fsi; 196 IsFile = Fsi is FileInfo; 197 IsBad = _isBad || !Fsi.IsReadable(); 198 TypeString = IsFile ? "file" : "directory"; 199 Icon = IsBad 200 ? Utils.AbbrIcon($"Server cannot read this {TypeString}", "⚠️") 201 : Utils.GetIconForFileType(Fsi); 202 Name = baseFsi.Name; // specifically want whatever its called in the directory we're looking at 203 IsDotFile = Name.StartsWith('.'); 204 Size = IsBad || !IsFile ? 0 : ((FileInfo)Fsi).Length; 205 TimeFmtDate = _dt.ToString("yyyy-MM-dd"); 206 TimeFmtTime = _dt.ToString("HH:mm"); 207 TimeUnix = $"{_dt.Subtract(DateTime.UnixEpoch).TotalSeconds:N0}"; 208 Href = IsBad 209 ? null 210 : String.Join('/', 211 System.IO.Path.Join(currentPath, Name) 212 .Split('/') 213 .Select(Uri.EscapeDataString) 214 ); 215 Trail = IsFile ? null : '/'; 216 } 217 } 218}