This commit is contained in:
lollipopkit
2022-12-11 15:31:12 +08:00
parent 7e01c4cbb3
commit cfd28c3009
20 changed files with 1023 additions and 810 deletions

View File

@@ -1,4 +1,5 @@
final _dockerImageReg = RegExp(r'(\S+) +(\S+) +(\S+) +(.+) +(\S+)');
class DockerImage {
final String repo;
final String tag;
@@ -44,4 +45,3 @@ class DockerImage {
);
}
}

View File

@@ -53,7 +53,7 @@ class DockerProvider extends BusyProvider {
final verRaw = await client!.run('docker version'.withLangExport).string;
if (verRaw.contains(_dockerNotFound)) {
error = DockerErr(type: DockerErrType.notInstalled);
notifyListeners();
setBusyState(false);
return;
}
@@ -65,6 +65,7 @@ class DockerProvider extends BusyProvider {
}
try {
setBusyState();
final cmd = _wrap(_dockerPS);
// run docker ps
@@ -97,7 +98,7 @@ class DockerProvider extends BusyProvider {
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
rethrow;
} finally {
notifyListeners();
setBusyState(false);
}
}
@@ -145,7 +146,8 @@ class DockerProvider extends BusyProvider {
if (code != 0) {
setBusyState(false);
return DockerErr(type: DockerErrType.unknown, message: errs.join('\n').trim());
return DockerErr(
type: DockerErrType.unknown, message: errs.join('\n').trim());
}
await refresh();
setBusyState(false);

View File

@@ -61,21 +61,26 @@ class _AptManagePageState extends State<AptManagePage>
_aptProvider.init(
si.client!,
si.status.sysVer.dist,
() =>
scrollController.jumpTo(scrollController.position.maxScrollExtent),
() => scrollController.jumpTo(scrollController.position.maxScrollExtent),
() => scrollControllerUpdate
.jumpTo(scrollController.position.maxScrollExtent),
onPwdRequest,
widget.spi.user);
widget.spi.user,
);
_aptProvider.refreshInstalled();
}
void onSubmitted() {
if (textController.text == '') {
showRoundDialog(context, s.attention, Text(s.fieldMustNotEmpty), [
showRoundDialog(
context,
s.attention,
Text(s.fieldMustNotEmpty),
[
TextButton(
onPressed: () => Navigator.of(context).pop(), child: Text(s.ok)),
]);
],
);
return;
}
Navigator.of(context).pop();
@@ -108,7 +113,8 @@ class _AptManagePageState extends State<AptManagePage>
s.ok,
style: const TextStyle(color: Colors.red),
)),
]);
],
);
return textController.text.trim();
}
@@ -142,7 +148,8 @@ class _AptManagePageState extends State<AptManagePage>
child: Text(
apt.error!,
textAlign: TextAlign.center,
)),
),
),
),
),
),
@@ -154,9 +161,8 @@ class _AptManagePageState extends State<AptManagePage>
}
if (apt.updateLog != null && apt.upgradeable == null) {
return SizedBox(
height: _media.size.height -
_media.padding.top -
_media.padding.bottom,
height:
_media.size.height - _media.padding.top - _media.padding.bottom,
child: ConstrainedBox(
constraints: const BoxConstraints.expand(),
child: SingleChildScrollView(
@@ -164,7 +170,8 @@ class _AptManagePageState extends State<AptManagePage>
controller: scrollControllerUpdate,
child: Text(apt.updateLog!),
),
));
),
);
}
return ListView(
padding: const EdgeInsets.all(13),
@@ -219,7 +226,8 @@ class _AptManagePageState extends State<AptManagePage>
controller: scrollController,
children: apt.upgradeable!
.map((e) => _buildUpdateItem(e, apt))
.toList()),
.toList(),
),
)
]
: [
@@ -232,7 +240,8 @@ class _AptManagePageState extends State<AptManagePage>
controller: scrollController,
child: Text(apt.upgradeLog!),
),
))
),
)
],
)
],

View File

@@ -54,8 +54,12 @@ class BackupPage extends StatelessWidget {
const SizedBox(height: 7),
const Divider(),
const SizedBox(height: 7),
_buildCard(s.backup, Icons.file_upload, media,
() => _showExportDialog(context, s))
_buildCard(
s.backup,
Icons.file_upload,
media,
() => _showExportDialog(context, s),
)
],
)),
);
@@ -91,7 +95,9 @@ class BackupPage extends StatelessWidget {
Future<void> _showExportDialog(BuildContext context, S s) async {
final exportFieldController = TextEditingController()
..text = _diyEncrtpt(json.encode(Backup(
..text = _diyEncrtpt(
json.encode(
Backup(
backupFormatVersion,
DateTime.now().toString().split('.').first,
server.fetch(),
@@ -100,7 +106,9 @@ class BackupPage extends StatelessWidget {
setting.primaryColor.fetch() ?? Colors.pinkAccent.value,
setting.serverStatusUpdateInterval.fetch() ?? 2,
setting.launchPage.fetch() ?? 0,
)));
),
),
);
await showRoundDialog(
context,
s.export,
@@ -115,12 +123,12 @@ class BackupPage extends StatelessWidget {
TextButton(
child: Text(s.copy),
onPressed: () {
Clipboard.setData(
ClipboardData(text: exportFieldController.text));
Clipboard.setData(ClipboardData(text: exportFieldController.text));
Navigator.pop(context);
},
),
]);
],
);
}
Future<void> _showImportDialog(BuildContext context, S s) async {
@@ -138,13 +146,15 @@ class BackupPage extends StatelessWidget {
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
child: Text(s.cancel),
),
TextButton(
onPressed: () async =>
await _import(importFieldController.text.trim(), context, s),
child: const Text('GO'),
)
]);
],
);
}
Future<void> _import(String text, BuildContext context, S s) async {
@@ -165,7 +175,10 @@ class BackupPage extends StatelessWidget {
}
await showRoundDialog(
context, s.attention, Text(s.restoreSureWithDate(backup.date)), [
context,
s.attention,
Text(s.restoreSureWithDate(backup.date)),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel),
@@ -190,7 +203,8 @@ class BackupPage extends StatelessWidget {
},
child: Text(s.ok),
),
]);
],
);
} catch (e) {
showSnackBar(context, Text(s.invalidJson));
return;

View File

@@ -47,12 +47,15 @@ class _ConvertPageState extends State<ConvertPage>
body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 7),
controller: ScrollController(),
child: Column(children: [
child: Column(
children: [
const SizedBox(height: 13),
_buildInputTop(),
_buildTypeOption(),
_buildResult(),
])),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
try {
@@ -119,7 +122,8 @@ class _ConvertPageState extends State<ConvertPage>
),
TextButton(
style: ButtonStyle(
foregroundColor: MaterialStateProperty.all(primaryColor)),
foregroundColor: MaterialStateProperty.all(primaryColor),
),
child: Icon(Icons.copy, semanticLabel: s.copy),
onPressed: () => Clipboard.setData(
ClipboardData(
@@ -136,13 +140,15 @@ class _ConvertPageState extends State<ConvertPage>
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(typeOption[_typeOptionIndex],
Text(
typeOption[_typeOptionIndex],
textScaleFactor: 1.0,
textAlign: TextAlign.left,
style: TextStyle(
fontSize: 16.0,
fontWeight: FontWeight.w500,
color: primaryColor)),
color: primaryColor),
),
Text(
s.currentMode,
textScaleFactor: 1.0,
@@ -153,7 +159,8 @@ class _ConvertPageState extends State<ConvertPage>
),
),
children: typeOption
.map((e) => ListTile(
.map(
(e) => ListTile(
title: Text(
e,
style: TextStyle(
@@ -161,7 +168,8 @@ class _ConvertPageState extends State<ConvertPage>
),
),
trailing: _buildRadio(typeOption.indexOf(e)),
))
),
)
.toList(),
),
);

View File

@@ -13,8 +13,10 @@ class _DebugPageState extends State<DebugPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:
AppBar(title: const Text('App log'), backgroundColor: Colors.black),
appBar: AppBar(
title: const Text('App log'),
backgroundColor: Colors.black,
),
body: _buildTerminal(context),
backgroundColor: Colors.black,
);
@@ -31,12 +33,15 @@ class _DebugPageState extends State<DebugPage> {
fontWeight: FontWeight.bold,
),
child: SingleChildScrollView(
child: Consumer<DebugProvider>(builder: (_, debug, __) {
child: Consumer<DebugProvider>(
builder: (_, debug, __) {
return Column(
mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start,
children: debug.widgets);
}),
children: debug.widgets,
);
},
),
),
),
);

View File

@@ -67,7 +67,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
actions: [
IconButton(
onPressed: () => docker.refresh(),
icon: const Icon(Icons.refresh))
icon: const Icon(Icons.refresh),
)
],
),
body: _buildMain(docker),
@@ -124,8 +125,13 @@ class _DockerManagePageState extends State<DockerManagePage> {
TextButton(
onPressed: () async {
Navigator.of(context).pop();
await _showAddCmdPreview(_buildAddCmd(imageCtrl.text.trim(),
nameCtrl.text.trim(), argsCtrl.text.trim()));
await _showAddCmdPreview(
_buildAddCmd(
imageCtrl.text.trim(),
nameCtrl.text.trim(),
argsCtrl.text.trim(),
),
);
},
child: Text(s.ok),
)
@@ -208,14 +214,17 @@ class _DockerManagePageState extends State<DockerManagePage> {
Navigator.of(context).pop();
Navigator.of(context).pop();
},
child: Text(s.cancel)),
child: Text(s.cancel),
),
TextButton(
onPressed: () => onSubmitted(),
child: Text(
s.ok,
style: const TextStyle(color: Colors.red),
)),
]);
),
),
],
);
return textController.text.trim();
}
@@ -286,8 +295,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
),
),
)
.toList(),
);
.toList());
}
Widget _buildLoading(DockerProvider docker) {
@@ -319,7 +327,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
),
TextButton(
onPressed: () => _showEditHostDialog(docker),
child: Text(s.dockerEditHost))
child: Text(s.dockerEditHost),
)
],
),
);
@@ -343,7 +352,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
child: Text(s.cancel),
),
],
);
}
@@ -399,14 +409,16 @@ class _DockerManagePageState extends State<DockerManagePage> {
return ExpansionTile(
title: Text(s.containerStatus),
subtitle: Text(_buildSubtitle(running), style: greyTextStyle),
children: running.map((item) {
children: running.map(
(item) {
return ListTile(
title: Text(item.image),
subtitle: Text(item.status),
trailing:
_buildMoreBtn(item.running, item.containerId, docker.isBusy),
);
}).toList(),
},
).toList(),
);
}

View File

@@ -139,7 +139,10 @@ class _MyHomePageState extends State<MyHomePage>
? Colors.white12
: Colors.black.withOpacity(0.07)
: Colors.transparent,
borderRadius: const BorderRadius.all(Radius.circular(50))),
borderRadius: const BorderRadius.all(
Radius.circular(50),
),
),
child: IconButton(
icon: Icon(item.icon),
tooltip: tabTitleName(context, idx),
@@ -164,12 +167,15 @@ class _MyHomePageState extends State<MyHomePage>
width: _width,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: tabItems.map((item) {
children: tabItems.map(
(item) {
int itemIndex = tabItems.indexOf(item);
return _buildItem(itemIndex, item, _selectIndex == itemIndex);
}).toList(),
},
).toList(),
),
));
),
);
}
Widget _buildDrawer() {
@@ -217,18 +223,25 @@ class _MyHomePageState extends State<MyHomePage>
leading: const Icon(Icons.info),
title: Text(s.feedback),
onTap: () => showRoundDialog(
context, s.feedback, Text(s.feedbackOnGithub), [
context,
s.feedback,
Text(s.feedbackOnGithub),
[
TextButton(
onPressed: () => Clipboard.setData(
const ClipboardData(text: issueUrl)),
child: Text(s.copy)),
child: Text(s.copy),
),
TextButton(
onPressed: () => openUrl(issueUrl),
child: Text(s.feedback)),
child: Text(s.feedback),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.close))
]),
child: Text(s.close),
)
],
),
),
ListTile(
leading: const Icon(Icons.snippet_folder),
@@ -274,7 +287,8 @@ class _MyHomePageState extends State<MyHomePage>
constraints: const BoxConstraints(maxHeight: 53, maxWidth: 53),
child: Container(
color: primaryColor,
)),
),
),
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 83, maxWidth: 83),
child: appIcon,

View File

@@ -52,7 +52,8 @@ class _PingPageState extends State<PingPage>
return Scaffold(
body: SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 7),
child: Column(children: [
child: Column(
children: [
const SizedBox(height: 13),
buildInput(context, _textEditingController,
hint: s.inputDomainHere,
@@ -69,7 +70,9 @@ class _PingPageState extends State<PingPage>
return _buildResultItem(result);
}),
),
])),
],
),
),
floatingActionButton: FloatingActionButton(
heroTag: 'ping fab',
onPressed: () {
@@ -87,19 +90,30 @@ class _PingPageState extends State<PingPage>
Widget _buildResultItem(PingResult result) {
final unknown = s.unknown;
final ms = s.ms;
return RoundRectCard(ListTile(
return RoundRectCard(
ListTile(
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
title: Text(result.serverName,
title: Text(
result.serverName,
style: TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor)),
fontSize: 18,
fontWeight: FontWeight.bold,
color: primaryColor,
),
),
subtitle: Text(
_buildPingSummary(result, unknown, ms),
style: summaryTextStyle,
),
trailing: Text(
'${s.pingAvg}${result.statistic?.avg?.toStringAsFixed(2) ?? s.unknown} $ms',
style: TextStyle(fontSize: 14, color: primaryColor)),
));
style: TextStyle(
fontSize: 14,
color: primaryColor,
),
),
),
);
}
String _buildPingSummary(PingResult result, String unknown, String ms) {

View File

@@ -51,7 +51,9 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
appBar: AppBar(
title: Text(s.edit, style: textSize18),
actions: [
widget.info != null
? IconButton(
tooltip: s.delete,
@@ -61,7 +63,8 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
},
icon: const Icon(Icons.delete))
: const SizedBox()
]),
],
),
body: ListView(
padding: const EdgeInsets.all(13),
children: [

View File

@@ -58,12 +58,15 @@ class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
child: Text(
s.edit,
style: _textStyle,
))
),
)
],
),
));
})
: Center(child: Text(s.noSavedPrivateKey));
: Center(
child: Text(s.noSavedPrivateKey),
);
},
),
floatingActionButton: FloatingActionButton(

View File

@@ -37,8 +37,9 @@ class _ServerDetailPageState extends State<ServerDetailPage>
Widget build(BuildContext context) {
return Consumer<ServerProvider>(builder: (_, provider, __) {
return _buildMainPage(
provider.servers
.firstWhere((e) => '${e.info.ip}:${e.info.port}' == widget.id),
provider.servers.firstWhere(
(e) => '${e.info.ip}:${e.info.port}' == widget.id,
),
);
});
}
@@ -212,7 +213,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
(ss.memory.cache * mb).convertBytes, pColor.withAlpha(77)),
_buildMemExplain(
((ss.memory.total - ss.memory.used) * mb).convertBytes,
progressColor.resolve(context))
progressColor.resolve(context),
)
],
),
const SizedBox(
@@ -367,7 +369,8 @@ class _ServerDetailPageState extends State<ServerDetailPage>
child: Text(ns.speedOut(device: device),
style: textSize11,
textAlign: TextAlign.right,
textScaleFactor: 1.0))
textScaleFactor: 1.0),
)
],
),
);

View File

@@ -63,12 +63,17 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
appBar: AppBar(
title: Text(s.edit, style: textSize18),
actions: [
widget.spi != null
? IconButton(
onPressed: () {
showRoundDialog(context, s.attention,
Text(s.sureToDeleteServer(widget.spi!.name)), [
showRoundDialog(
context,
s.attention,
Text(s.sureToDeleteServer(widget.spi!.name)),
[
TextButton(
onPressed: () {
_serverProvider.delServer(widget.spi!);
@@ -78,15 +83,20 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
child: Text(
s.ok,
style: const TextStyle(color: Colors.red),
)),
),
),
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel))
]);
child: Text(s.cancel),
)
],
);
},
icon: const Icon(Icons.delete))
icon: const Icon(Icons.delete),
)
: const SizedBox()
]),
],
),
body: SingleChildScrollView(
padding: const EdgeInsets.all(17),
child: Column(
@@ -148,7 +158,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
)
: const SizedBox(),
usePublicKey
? Consumer<PrivateKeyProvider>(builder: (_, key, __) {
? Consumer<PrivateKeyProvider>(
builder: (_, key, __) {
for (var item in key.infos) {
if (item.id == widget.spi?.pubKeyId) {
_pubKeyIndex ??= key.infos.indexOf(item);
@@ -162,16 +173,19 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
trailing: _buildRadio(key.infos.indexOf(e), e)),
)
.toList();
tiles.add(ListTile(
tiles.add(
ListTile(
title: Text(s.addPrivateKey),
contentPadding: EdgeInsets.zero,
trailing: IconButton(
icon: const Icon(Icons.add),
onPressed: () => AppRoute(const PrivateKeyEditPage(),
onPressed: () => AppRoute(
const PrivateKeyEditPage(),
'private key edit page')
.go(context),
),
));
),
);
return ExpansionTile(
textColor: primaryColor,
iconColor: primaryColor,
@@ -183,7 +197,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
),
children: tiles,
);
})
},
)
: const SizedBox()
],
),
@@ -208,7 +223,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
onPressed: () => Navigator.of(context).pop(true),
child: Text(s.cancel))
],
barrierDismiss: false);
barrierDismiss: false,
);
if (cancel ?? true) {
return;
}
@@ -235,7 +251,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
port: int.parse(portController.text),
user: usernameController.text,
pwd: authorization,
pubKeyId: usePublicKey ? _keyInfo!.id : null);
pubKeyId: usePublicKey ? _keyInfo!.id : null,
);
if (widget.spi == null) {
_serverProvider.addServer(spi);

View File

@@ -55,7 +55,8 @@ class _ServerPageState extends State<ServerPage>
@override
Widget build(BuildContext context) {
super.build(context);
final child = Consumer<ServerProvider>(builder: (_, pro, __) {
final child = Consumer<ServerProvider>(
builder: (_, pro, __) {
if (pro.servers.isEmpty) {
return Center(
child: Text(
@@ -78,7 +79,8 @@ class _ServerPageState extends State<ServerPage>
height: 3,
),
);
});
},
);
return Scaffold(
body: child,
floatingActionButton: FloatingActionButton(
@@ -104,7 +106,8 @@ class _ServerPageState extends State<ServerPage>
child: Padding(
padding: const EdgeInsets.all(13),
child: _buildRealServerCard(
si.status, si.info.name, si.connectionState, si.info)),
si.status, si.info.name, si.connectionState, si.info),
),
onTap: () => AppRoute(ServerDetailPage('${si.info.ip}:${si.info.port}'),
'server detail page')
.go(context),

View File

@@ -71,7 +71,8 @@ class _SettingPageState extends State<SettingPage> {
}
Widget _buildCheckUpdate() {
return Consumer<AppProvider>(builder: (_, app, __) {
return Consumer<AppProvider>(
builder: (_, app, __) {
String display;
if (app.newestBuild != null) {
if (app.newestBuild! > BuildData.build) {
@@ -90,8 +91,10 @@ class _SettingPageState extends State<SettingPage> {
style: textSize13,
textAlign: TextAlign.start,
),
onTap: () => doUpdate(context, force: true));
});
onTap: () => doUpdate(context, force: true),
);
},
);
}
Widget _buildUpdateInterval() {
@@ -161,10 +164,8 @@ class _SettingPageState extends State<SettingPage> {
s.appPrimaryColor,
style: textSize13,
),
children: [
_buildAppColorPicker(priColor),
_buildColorPickerConfirmBtn()
]);
children: [_buildAppColorPicker(priColor), _buildColorPickerConfirmBtn()],
);
}
Widget _buildAppColorPicker(Color selected) {
@@ -204,7 +205,8 @@ class _SettingPageState extends State<SettingPage> {
),
),
children: tabs
.map((e) => ListTile(
.map(
(e) => ListTile(
contentPadding: EdgeInsets.zero,
title: Text(
tabTitleName(context, tabs.indexOf(e)),
@@ -213,7 +215,8 @@ class _SettingPageState extends State<SettingPage> {
color: _theme.textTheme.bodyText2!.color!.withAlpha(177)),
),
trailing: _buildRadio(tabs.indexOf(e)),
))
),
)
.toList(),
);
}

View File

@@ -152,7 +152,10 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
onTap: () {
Navigator.of(context).pop();
showRoundDialog(
context, s.sureDelete(fileName), const SizedBox(), [
context,
s.sureDelete(fileName),
const SizedBox(),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
@@ -164,7 +167,8 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
},
child: Text(s.ok),
),
]);
],
);
},
),
ListTile(
@@ -172,13 +176,15 @@ class _SFTPDownloadedPageState extends State<SFTPDownloadedPage> {
title: Text(s.open),
onTap: () {
shareFiles(context, [file.absolute.path]);
}),
},
),
],
),
[
TextButton(
onPressed: (() => Navigator.of(context).pop()),
child: Text(s.close))
]);
],
);
}
}

View File

@@ -58,7 +58,8 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
Widget _wrapInCard(SftpDownloadStatus status, String? subtitle,
{Widget? trailing}) {
return RoundRectCard(ListTile(
return RoundRectCard(
ListTile(
title: Text(status.fileName),
subtitle: subtitle == null
? null
@@ -67,7 +68,8 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
style: grey,
),
trailing: trailing,
));
),
);
}
Widget _buildItem(SftpDownloadStatus status) {
@@ -77,11 +79,14 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
switch (status.status) {
case SftpWorkerStatus.finished:
final time = status.spentTime.toString();
return _wrapInCard(status,
return _wrapInCard(
status,
'${s.downloadFinished} ${s.spentTime(time == 'null' ? s.unknown : (time.substring(0, time.length - 7)))}',
trailing: IconButton(
onPressed: () => shareFiles(context, [status.item.localPath]),
icon: const Icon(Icons.open_in_new)));
icon: const Icon(Icons.open_in_new),
),
);
case SftpWorkerStatus.downloading:
return _wrapInCard(
status,
@@ -94,11 +99,14 @@ class _SFTPDownloadingPageState extends State<SFTPDownloadingPage> {
case SftpWorkerStatus.sshConnectted:
return _wrapInCard(status, s.sftpSSHConnected, trailing: loadingIcon);
default:
return _wrapInCard(status, s.unknown,
return _wrapInCard(
status,
s.unknown,
trailing: const Icon(
Icons.error,
size: 40,
));
),
);
}
}
}

View File

@@ -79,10 +79,12 @@ class _SFTPPageState extends State<SFTPPage> {
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.close))
])),
],
)),
icon: const Icon(Icons.add),
)
]),
],
),
body: _buildFileView(),
);
}
@@ -157,7 +159,8 @@ class _SFTPPageState extends State<SFTPPage> {
},
),
),
onRefresh: () => listDir(path: _status.path?.path));
onRefresh: () => listDir(path: _status.path?.path),
);
}
}
@@ -189,16 +192,20 @@ class _SFTPPageState extends State<SFTPPage> {
),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel))
]);
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel))
],
);
}
void download(BuildContext context, SftpName name) {
showRoundDialog(context, s.download,
Text('${s.dl2Local(name.filename)}\n${s.keepForeground}'), [
showRoundDialog(
context,
s.download,
Text('${s.dl2Local(name.filename)}\n${s.keepForeground}'),
[
TextButton(
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel)),
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
TextButton(
onPressed: () async {
Navigator.of(context).pop();
@@ -207,32 +214,52 @@ class _SFTPPageState extends State<SFTPPage> {
prePath + (prePath.endsWith('/') ? '' : '/') + name.filename;
final local = '${(await sftpDownloadDir).path}$remotePath';
final pubKeyId = _status.spi!.pubKeyId;
locator<SftpDownloadProvider>().add(
DownloadItem(_status.spi!, remotePath, local),
DownloadItem(
_status.spi!,
remotePath,
local,
),
key: pubKeyId == null
? null
: locator<PrivateKeyStore>().get(pubKeyId).privateKey);
: locator<PrivateKeyStore>().get(pubKeyId).privateKey,
);
Navigator.of(context).pop();
showRoundDialog(context, s.goSftpDlPage, const SizedBox(), [
showRoundDialog(
context,
s.goSftpDlPage,
const SizedBox(),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
child: Text(s.cancel),
),
TextButton(
onPressed: () {
Navigator.of(context).pop();
AppRoute(const SFTPDownloadingPage(), 'sftp downloading')
.go(context);
},
child: Text(s.ok))
]);
child: Text(s.ok),
)
],
);
},
child: Text(s.download))
]);
child: Text(s.download),
)
],
);
}
void delete(BuildContext context, SftpName file) {
Navigator.of(context).pop();
showRoundDialog(context, s.attention, Text(s.sureDelete(file.filename)), [
showRoundDialog(
context,
s.attention,
Text(s.sureDelete(file.filename)),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('Cancel')),
@@ -245,8 +272,10 @@ class _SFTPPageState extends State<SFTPPage> {
child: Text(
s.delete,
style: const TextStyle(color: Colors.red),
)),
]);
),
),
],
);
}
void mkdir(BuildContext context) {
@@ -264,16 +293,21 @@ class _SFTPPageState extends State<SFTPPage> {
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
child: Text(s.cancel),
),
TextButton(
onPressed: () {
if (textController.text == '') {
showRoundDialog(
context, s.attention, Text(s.fieldMustNotEmpty), [
context,
s.attention,
Text(s.fieldMustNotEmpty),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.ok)),
]);
],
);
return;
}
_status.client!
@@ -284,8 +318,10 @@ class _SFTPPageState extends State<SFTPPage> {
child: Text(
s.ok,
style: const TextStyle(color: Colors.red),
)),
]);
),
),
],
);
}
void newFile(BuildContext context) {
@@ -303,16 +339,22 @@ class _SFTPPageState extends State<SFTPPage> {
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel)),
child: Text(s.cancel),
),
TextButton(
onPressed: () async {
if (textController.text == '') {
showRoundDialog(
context, s.attention, Text(s.fieldMustNotEmpty), [
context,
s.attention,
Text(s.fieldMustNotEmpty),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.ok)),
]);
child: Text(s.ok),
),
],
);
return;
}
(await _status.client!
@@ -324,8 +366,10 @@ class _SFTPPageState extends State<SFTPPage> {
child: Text(
s.ok,
style: const TextStyle(color: Colors.red),
)),
]);
),
),
],
);
}
void rename(BuildContext context, SftpName file) {
@@ -348,23 +392,29 @@ class _SFTPPageState extends State<SFTPPage> {
onPressed: () async {
if (textController.text == '') {
showRoundDialog(
context, s.attention, Text(s.fieldMustNotEmpty), [
context,
s.attention,
Text(s.fieldMustNotEmpty),
[
TextButton(
onPressed: () => Navigator.of(context).pop(),
child: Text(s.ok)),
]);
child: Text(s.ok),
),
],
);
return;
}
await _status.client!
.rename(file.filename, textController.text);
await _status.client!.rename(file.filename, textController.text);
Navigator.of(context).pop();
listDir();
},
child: Text(
s.rename,
style: const TextStyle(color: Colors.red),
)),
]);
),
),
],
);
}
Future<void> listDir({String? path, SSHClient? client}) async {
@@ -388,10 +438,17 @@ class _SFTPPageState extends State<SFTPPage> {
});
}
} catch (e) {
await showRoundDialog(context, s.error, Text(e.toString()), [
await showRoundDialog(
context,
s.error,
Text(e.toString()),
[
TextButton(
onPressed: () => Navigator.of(context).pop(), child: Text(s.ok))
]);
onPressed: () => Navigator.of(context).pop(),
child: Text(s.ok),
)
],
);
if (_status.path!.undo()) {
await listDir();
}
@@ -424,7 +481,8 @@ class _SFTPPageState extends State<SFTPPage> {
.servers
.firstWhere((s) => s.info == spi)
.client,
path: '/');
path: '/',
);
},
);
}

View File

@@ -41,7 +41,9 @@ class _SnippetEditPageState extends State<SnippetEditPage>
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(s.edit, style: textSize18), actions: [
appBar: AppBar(
title: Text(s.edit, style: textSize18),
actions: [
widget.snippet != null
? IconButton(
onPressed: () {
@@ -51,7 +53,8 @@ class _SnippetEditPageState extends State<SnippetEditPage>
tooltip: s.delete,
icon: const Icon(Icons.delete))
: const SizedBox()
]),
],
),
body: ListView(
padding: const EdgeInsets.all(13),
children: [

View File

@@ -59,7 +59,8 @@ class _SnippetListPageState extends State<SnippetListPage> {
itemCount: key.snippets.length,
itemExtent: 57,
itemBuilder: (context, idx) {
return RoundRectCard(Padding(
return RoundRectCard(
Padding(
padding: roundRectCardPadding,
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
@@ -69,7 +70,8 @@ class _SnippetListPageState extends State<SnippetListPage> {
key.snippets[idx].name,
textAlign: TextAlign.center,
),
Row(children: [
Row(
children: [
TextButton(
onPressed: () => AppRoute(
SnippetEditPage(
@@ -79,7 +81,8 @@ class _SnippetListPageState extends State<SnippetListPage> {
child: Text(
s.edit,
style: _textStyle,
)),
),
),
TextButton(
onPressed: () {
final snippet = key.snippets[idx];
@@ -92,27 +95,37 @@ class _SnippetListPageState extends State<SnippetListPage> {
child: Text(
s.run,
style: _textStyle,
))
])
),
)
],
)
],
),
));
})
: Center(child: Text(s.noSavedSnippet));
),
);
},
)
: Center(
child: Text(s.noSavedSnippet),
);
},
);
}
void _showRunDialog(Snippet snippet) {
showRoundDialog(context, s.chooseDestination,
Consumer<ServerProvider>(builder: (_, provider, __) {
showRoundDialog(
context,
s.chooseDestination,
Consumer<ServerProvider>(
builder: (_, provider, __) {
if (provider.servers.isEmpty) {
return Text(s.noServerAvailable);
}
_selectedIndex = provider.servers.first.info;
return SizedBox(
height: 111,
child: Stack(children: [
child: Stack(
children: [
Positioned(
top: 36,
bottom: 36,
@@ -142,24 +155,39 @@ class _SnippetListPageState extends State<SnippetListPage> {
),
childCount: provider.servers.length),
)
]));
}), [
],
),
);
},
),
[
TextButton(
onPressed: () async => run(context, snippet), child: Text(s.run)),
onPressed: () async => run(context, snippet),
child: Text(s.run),
),
TextButton(
onPressed: () => Navigator.of(context).pop(), child: Text(s.cancel)),
]);
onPressed: () => Navigator.of(context).pop(),
child: Text(s.cancel),
),
],
);
}
Future<void> run(BuildContext context, Snippet snippet) async {
final result = await locator<ServerProvider>()
.runSnippet(widget.spi ?? _selectedIndex, snippet);
if (result != null) {
showRoundDialog(context, s.result,
Text(result, style: const TextStyle(fontSize: 13)), [
showRoundDialog(
context,
s.result,
Text(result, style: const TextStyle(fontSize: 13)),
[
TextButton(
onPressed: () => Navigator.of(context).pop(), child: Text(s.close))
]);
onPressed: () => Navigator.of(context).pop(),
child: Text(s.close),
)
],
);
}
}
}