opt.: file view (#249)
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import 'package:dartssh2/dartssh2.dart';
|
||||
|
||||
extension SftpFile on SftpFileMode {
|
||||
extension SftpFileX on SftpFileMode {
|
||||
String get str {
|
||||
final user = _getRoleMode(userRead, userWrite, userExecute);
|
||||
final group = _getRoleMode(groupRead, groupWrite, groupExecute);
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 703;
|
||||
static const int build = 704;
|
||||
static const String engine = "3.16.7";
|
||||
static const String buildAt = "2024-01-14 19:40:49";
|
||||
static const int modifications = 6;
|
||||
static const String buildAt = "2024-01-15 18:40:44";
|
||||
static const int modifications = 3;
|
||||
static const int script = 34;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "SFTP Verbunden",
|
||||
"showDistLogo": "Distributionslogo anzeigen",
|
||||
"shutdown": "Abschaltung",
|
||||
"size": "Größe",
|
||||
"snippet": "Snippet",
|
||||
"speed": "Tempo",
|
||||
"spentTime": "Benötigte Zeit: {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100% (Originalgröße), funktioniert nur auf der Serverseite Teil der Schrift, nicht empfohlen zu ändern.",
|
||||
"theme": "Themen",
|
||||
"themeMode": "Themen-Modus",
|
||||
"time": "Zeit",
|
||||
"times": "x",
|
||||
"traffic": "Durchflussmenge",
|
||||
"ttl": "ttl",
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "SFTP Connected",
|
||||
"showDistLogo": "Show distribution logo",
|
||||
"shutdown": "Shutdown",
|
||||
"size": "Size",
|
||||
"snippet": "Snippet",
|
||||
"speed": "Speed",
|
||||
"spentTime": "Spent time: {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100% (original size), only works on server page part of the font, not recommended to change.",
|
||||
"theme": "Theme",
|
||||
"themeMode": "Theme mode",
|
||||
"time": "Time",
|
||||
"times": "Times",
|
||||
"traffic": "Traffic",
|
||||
"ttl": "ttl",
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "SFTP connecté",
|
||||
"showDistLogo": "Afficher le logo de la distribution",
|
||||
"shutdown": "Éteindre",
|
||||
"size": "Taille",
|
||||
"snippet": "Extrait",
|
||||
"speed": "Vitesse",
|
||||
"spentTime": "Temps écoulé : {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100% (taille d'origine), fonctionne uniquement sur la partie police de caractères de la page du serveur, il n'est pas recommandé de la modifier.",
|
||||
"theme": "Thème",
|
||||
"themeMode": "Mode du thème",
|
||||
"time": "L'heure",
|
||||
"times": "Fois",
|
||||
"traffic": "Trafic",
|
||||
"ttl": "ttl",
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "Sftp terhubung",
|
||||
"showDistLogo": "Tampilkan logo distribusi",
|
||||
"shutdown": "Matikan",
|
||||
"size": "Ukuran",
|
||||
"snippet": "Snippet",
|
||||
"speed": "Kecepatan",
|
||||
"spentTime": "Menghabiskan waktu: {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100% (ukuran asli), hanya berfungsi pada bagian halaman server font, tidak disarankan untuk diubah.",
|
||||
"theme": " Tema",
|
||||
"themeMode": "Mode tema",
|
||||
"time": "Waktu",
|
||||
"times": "Waktu",
|
||||
"traffic": "Lalu lintas",
|
||||
"ttl": "ttl",
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "SFTP 已连接...",
|
||||
"showDistLogo": "显示发行版 Logo",
|
||||
"shutdown": "关机",
|
||||
"size": "大小",
|
||||
"snippet": "代码片段",
|
||||
"speed": "速度",
|
||||
"spentTime": "耗时: {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100%(原大小),仅作用于服务器页面部分字体,不建议修改。",
|
||||
"theme": "主题",
|
||||
"themeMode": "主题模式",
|
||||
"time": "时间",
|
||||
"times": "次",
|
||||
"traffic": "流量",
|
||||
"ttl": "缓存时间",
|
||||
|
||||
@@ -208,6 +208,7 @@
|
||||
"sftpSSHConnected": "SFTP 已連接...",
|
||||
"showDistLogo": "顯示發行版 Logo",
|
||||
"shutdown": "关机",
|
||||
"size": "大小",
|
||||
"snippet": "程式片段",
|
||||
"speed": "速度",
|
||||
"spentTime": "耗時: {time}",
|
||||
@@ -229,6 +230,7 @@
|
||||
"textScalerTip": "1.0 => 100%(原大小),僅作用於伺服器頁面部分字體,不建議修改。",
|
||||
"theme": "主題",
|
||||
"themeMode": "主題模式",
|
||||
"time": "時間",
|
||||
"times": "次",
|
||||
"traffic": "流量",
|
||||
"ttl": "緩存時間",
|
||||
|
||||
@@ -132,7 +132,7 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
||||
() => delScripts = !delScripts,
|
||||
),
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
subtitle: Text(l10n.deleteScripts),
|
||||
title: Text(l10n.deleteScripts),
|
||||
tileColor: Colors.transparent,
|
||||
contentPadding: EdgeInsets.zero,
|
||||
)
|
||||
@@ -142,13 +142,15 @@ class _ServerEditPageState extends State<ServerEditPage> {
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
if (delScripts) {
|
||||
context.showLoadingDialog();
|
||||
const cmd =
|
||||
'rm ${ShellFunc.srvBoxDir}/mobile_v*.sh';
|
||||
await widget.spi?.server?.client?.run(cmd);
|
||||
context.pop();
|
||||
}
|
||||
Pros.server.delServer(widget.spi!.id);
|
||||
context.pop();
|
||||
context.pop(true);
|
||||
},
|
||||
child: Text(l10n.ok, style: UIs.textRed),
|
||||
|
||||
@@ -39,6 +39,8 @@ class LocalStoragePage extends StatefulWidget {
|
||||
class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
LocalPath? _path;
|
||||
|
||||
final _sortType = ValueNotifier(_SortType.name);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -73,12 +75,44 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
IconButton(
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () => AppRoute.sftpMission().go(context),
|
||||
)
|
||||
),
|
||||
ValueListenableBuilder<_SortType>(
|
||||
valueListenable: _sortType,
|
||||
builder: (context, value, child) {
|
||||
return PopupMenuButton<_SortType>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: _SortType.name,
|
||||
child: Text(l10n.name),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SortType.size,
|
||||
child: Text(l10n.size),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SortType.time,
|
||||
child: Text(l10n.time),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
_sortType.value = value;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: FadeIn(
|
||||
key: UniqueKey(),
|
||||
child: _buildBody(),
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _sortType,
|
||||
builder: (_, val, __) {
|
||||
return _buildBody();
|
||||
},
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: SafeArea(child: _buildPath()),
|
||||
);
|
||||
@@ -129,7 +163,8 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
);
|
||||
}
|
||||
final dir = Directory(_path!.path);
|
||||
final files = dir.listSync();
|
||||
final tempFiles = dir.listSync();
|
||||
final files = _sortType.value.sort(tempFiles);
|
||||
return ListView.builder(
|
||||
itemCount: files.length,
|
||||
padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 7),
|
||||
@@ -142,7 +177,7 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
return CardX(
|
||||
child: ListTile(
|
||||
leading: isDir
|
||||
? const Icon(Icons.folder)
|
||||
? const Icon(Icons.folder_open)
|
||||
: const Icon(Icons.insert_drive_file),
|
||||
title: Text(fileName),
|
||||
subtitle: isDir
|
||||
@@ -350,3 +385,26 @@ class _LocalStoragePageState extends State<LocalStoragePage> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum _SortType {
|
||||
name,
|
||||
size,
|
||||
time,
|
||||
;
|
||||
|
||||
List<FileSystemEntity> sort(List<FileSystemEntity> files) {
|
||||
switch (this) {
|
||||
case _SortType.name:
|
||||
files.sort((a, b) => a.path.compareTo(b.path));
|
||||
break;
|
||||
case _SortType.size:
|
||||
files.sort((a, b) => a.statSync().size.compareTo(b.statSync().size));
|
||||
break;
|
||||
case _SortType.time:
|
||||
files.sort(
|
||||
(a, b) => a.statSync().modified.compareTo(b.statSync().modified));
|
||||
break;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +50,8 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
final _status = SftpBrowserStatus();
|
||||
late final _client = widget.spi.server?.client;
|
||||
|
||||
final _sortType = ValueNotifier(_SortType.name);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
@@ -67,6 +69,33 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
icon: const Icon(Icons.downloading),
|
||||
onPressed: () => AppRoute.sftpMission().go(context),
|
||||
),
|
||||
ValueListenableBuilder<_SortType>(
|
||||
valueListenable: _sortType,
|
||||
builder: (context, value, child) {
|
||||
return PopupMenuButton<_SortType>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: _SortType.name,
|
||||
child: Text(l10n.name),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SortType.size,
|
||||
child: Text(l10n.size),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: _SortType.time,
|
||||
child: Text(l10n.time),
|
||||
),
|
||||
];
|
||||
},
|
||||
onSelected: (value) {
|
||||
_sortType.value = value;
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
body: _buildFileView(),
|
||||
@@ -244,10 +273,16 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
return RefreshIndicator(
|
||||
child: FadeIn(
|
||||
key: Key(widget.spi.name + _status.path!.path),
|
||||
child: ListView.builder(
|
||||
itemCount: _status.files!.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
itemBuilder: (_, index) => _buildItem(_status.files![index]),
|
||||
child: ValueListenableBuilder(
|
||||
valueListenable: _sortType,
|
||||
builder: (_, sortType, __) {
|
||||
final files = sortType.sort(_status.files!);
|
||||
return ListView.builder(
|
||||
itemCount: files.length,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
|
||||
itemBuilder: (_, index) => _buildItem(files[index]),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
onRefresh: () => _listDir(),
|
||||
@@ -263,7 +298,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
);
|
||||
return CardX(
|
||||
child: ListTile(
|
||||
leading: Icon(isDir ? Icons.folder : Icons.insert_drive_file),
|
||||
leading: Icon(isDir ? Icons.folder_outlined : Icons.insert_drive_file),
|
||||
title: Text(file.filename),
|
||||
trailing: trailing,
|
||||
subtitle: isDir
|
||||
@@ -755,3 +790,27 @@ String _getTime(int? unixMill) {
|
||||
.toString()
|
||||
.replaceFirst('.000', '');
|
||||
}
|
||||
|
||||
enum _SortType {
|
||||
name,
|
||||
time,
|
||||
size,
|
||||
;
|
||||
|
||||
List<SftpName> sort(List<SftpName> files) {
|
||||
switch (this) {
|
||||
case _SortType.name:
|
||||
files.sort((a, b) => a.filename.compareTo(b.filename));
|
||||
break;
|
||||
case _SortType.time:
|
||||
files.sort(
|
||||
(a, b) => (a.attr.modifyTime ?? 0).compareTo(b.attr.modifyTime ?? 0),
|
||||
);
|
||||
break;
|
||||
case _SortType.size:
|
||||
files.sort((a, b) => (a.attr.size ?? 0).compareTo(b.attr.size ?? 0));
|
||||
break;
|
||||
}
|
||||
return files;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user