Merge branch 'main' of github.com:lollipopkit/flutter_server_box

This commit is contained in:
lollipopkit
2024-02-18 10:33:55 +08:00
23 changed files with 257 additions and 48 deletions

View File

@@ -206,7 +206,7 @@ class _ServerPageState extends State<ServerPage>
late final List<Widget> children;
if (srv.state == ServerState.finished) {
if (cardStatus.value.flip) {
children = [title, ..._buildFlipedCard(srv)];
children = [title, ..._buildFlippedCard(srv)];
} else {
children = [title, ..._buildNormalCard(srv.status, srv.spi)];
}
@@ -228,7 +228,7 @@ class _ServerPageState extends State<ServerPage>
);
}
List<Widget> _buildFlipedCard(Server srv) {
List<Widget> _buildFlippedCard(Server srv) {
return [
UIs.height13,
Row(

View File

@@ -855,6 +855,7 @@ class _SettingPageState extends State<SettingPage> {
children: [
_buildSftpRmrDir(),
_buildSftpOpenLastPath(),
_buildSftpShowFoldersFirst(),
].map((e) => CardX(child: e)).toList(),
);
}
@@ -867,6 +868,13 @@ class _SettingPageState extends State<SettingPage> {
);
}
Widget _buildSftpShowFoldersFirst() {
return ListTile(
title: Text(l10n.sftpShowFoldersFirst),
trailing: StoreSwitch(prop: _setting.sftpShowFoldersFirst),
);
}
Widget _buildNetViewType() {
final items = NetViewType.values
.map((e) => PopupMenuItem(

View File

@@ -33,11 +33,14 @@ class SSHPage extends StatefulWidget {
final ServerPrivateInfo spi;
final String? initCmd;
final bool pop;
final Function()? onSessionEnd;
const SSHPage({
super.key,
required this.spi,
this.initCmd,
this.pop = true,
this.onSessionEnd,
});
@override
@@ -103,7 +106,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
_terminalTheme = _isDark ? TerminalThemes.dark : TerminalThemes.light;
// Because the virtual keyboard only displayed on mobile devices
if (isMobile) {
if (isMobile || isDebuggingMobileLayoutOnDesktop) {
_virtKeyWidth = _media.size.width / 7;
_virtKeysHeight = _media.size.height * 0.043 * _virtKeysList.length;
}
@@ -115,7 +118,9 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
Widget child = Scaffold(
backgroundColor: _terminalTheme.background,
body: _buildBody(),
bottomNavigationBar: isDesktop ? null : _buildBottom(),
bottomNavigationBar: isDesktop && !isDebuggingMobileLayoutOnDesktop
? null
: _buildBottom(),
);
if (isIOS) {
child = AnnotatedRegion(
@@ -229,10 +234,12 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
void _doVirtualKey(VirtKey item) {
if (item.func != null) {
HapticFeedback.mediumImpact();
_doVirtualKeyFunc(item.func!);
return;
}
if (item.key != null) {
HapticFeedback.mediumImpact();
_doVirtualKeyInput(item.key!);
}
}
@@ -381,6 +388,7 @@ class _SSHPageState extends State<SSHPage> with AutomaticKeepAliveClientMixin {
if (mounted && widget.pop) {
context.pop();
}
widget.onSessionEnd?.call();
}
void _listen(Stream<Uint8List>? stream) {

View File

@@ -71,6 +71,7 @@ class _SSHTabPageState extends State<SSHTabPage>
if (confirm != true) {
return;
}
// debugPrint("Removing a tab whose tabId = $e");
_tabIds.remove(e);
_refreshTabs();
},
@@ -104,6 +105,12 @@ class _SSHTabPageState extends State<SSHTabPage>
key: key,
spi: spi,
pop: false,
onSessionEnd: () {
// debugPrint("Session done received on page whose tabId = $name");
// debugPrint("key = $key");
_tabIds.remove(name);
_refreshTabs();
},
);
_refreshTabs();
_tabController.animateTo(_tabIds.length - 1);

View File

@@ -8,7 +8,9 @@ import 'package:toolbox/core/extension/context/dialog.dart';
import 'package:toolbox/core/extension/context/locale.dart';
import 'package:toolbox/core/extension/context/snackbar.dart';
import 'package:toolbox/core/extension/sftpfile.dart';
import 'package:toolbox/core/utils/comparator.dart';
import 'package:toolbox/core/utils/platform/base.dart';
import 'package:toolbox/data/res/color.dart';
import 'package:toolbox/data/res/logger.dart';
import 'package:toolbox/data/res/misc.dart';
import 'package:toolbox/data/res/provider.dart';
@@ -50,7 +52,8 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
final _status = SftpBrowserStatus();
late final _client = widget.spi.server?.client;
final _sortType = ValueNotifier(_SortType.name);
final _sortOption =
ValueNotifier(_SortOption(sortBy: _SortType.name, reversed: false));
@override
Widget build(BuildContext context) {
@@ -69,29 +72,47 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
icon: const Icon(Icons.downloading),
onPressed: () => AppRoute.sftpMission().go(context),
),
ValueListenableBuilder<_SortType>(
valueListenable: _sortType,
ValueListenableBuilder(
valueListenable: _sortOption,
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),
),
final currentSelectedOption = _sortOption.value;
final options = [
(_SortType.name, l10n.name),
(_SortType.size, l10n.size),
(_SortType.time, l10n.time),
];
return options.map((r) {
final (type, name) = r;
return PopupMenuItem(
value: type,
child: Text(
type == currentSelectedOption.sortBy
? "$name (${currentSelectedOption.reversed ? '-' : '+'})"
: name,
style: TextStyle(
color: type == currentSelectedOption.sortBy
? primaryColor
: null,
fontWeight: type == currentSelectedOption.sortBy
? FontWeight.bold
: null,
),
),
);
}).toList();
},
onSelected: (value) {
_sortType.value = value;
onSelected: (sortBy) {
final oldValue = _sortOption.value;
if (oldValue.sortBy == sortBy) {
_sortOption.value = _SortOption(
sortBy: sortBy, reversed: !oldValue.reversed);
} else {
_sortOption.value =
_SortOption(sortBy: sortBy, reversed: false);
}
},
);
},
@@ -274,9 +295,10 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
child: FadeIn(
key: Key(widget.spi.name + _status.path!.path),
child: ValueListenableBuilder(
valueListenable: _sortType,
builder: (_, sortType, __) {
final files = sortType.sort(_status.files!);
valueListenable: _sortOption,
builder: (_, sortOption, __) {
final files = sortOption.sortBy
.sort(_status.files!, reversed: sortOption.reversed);
return ListView.builder(
itemCount: files.length,
padding: const EdgeInsets.symmetric(horizontal: 7, vertical: 3),
@@ -797,20 +819,51 @@ enum _SortType {
size,
;
List<SftpName> sort(List<SftpName> files) {
List<SftpName> sort(List<SftpName> files, {bool reversed = false}) {
var comparator = ChainComparator<SftpName>.create();
if (Stores.setting.sftpShowFoldersFirst.fetch()) {
comparator = comparator.thenTrueFirst((x) => x.attr.isDirectory);
}
switch (this) {
case _SortType.name:
files.sort((a, b) => a.filename.compareTo(b.filename));
files.sort(
comparator
.thenWithComparator(
(a, b) => Comparators.compareStringCaseInsensitive()(
a.filename, b.filename),
reversed: reversed,
)
.compare,
);
break;
case _SortType.time:
files.sort(
(a, b) => (a.attr.modifyTime ?? 0).compareTo(b.attr.modifyTime ?? 0),
comparator
.thenCompareBy<num>(
(x) => x.attr.modifyTime ?? 0,
reversed: reversed,
)
.compare,
);
break;
case _SortType.size:
files.sort((a, b) => (a.attr.size ?? 0).compareTo(b.attr.size ?? 0));
files.sort(
comparator
.thenCompareBy<num>(
(x) => x.attr.size ?? 0,
reversed: reversed,
)
.compare,
);
break;
}
return files;
}
}
class _SortOption {
final _SortType sortBy;
final bool reversed;
_SortOption({required this.sortBy, required this.reversed});
}