mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 15:24:35 +01:00
i18n support
This commit is contained in:
@@ -4,6 +4,7 @@ import 'package:clipboard/clipboard.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:toolbox/core/utils.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/view/widget/input_field.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
|
||||
@@ -20,13 +21,8 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
late TextEditingController _textEditingControllerResult;
|
||||
late MediaQueryData _media;
|
||||
late ThemeData _theme;
|
||||
late S s;
|
||||
|
||||
static const List<String> _typeOption = [
|
||||
'base64 decode',
|
||||
'base64 encode',
|
||||
'URL encode',
|
||||
'URL decode'
|
||||
];
|
||||
int _typeOptionIndex = 0;
|
||||
|
||||
@override
|
||||
@@ -41,6 +37,7 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
super.didChangeDependencies();
|
||||
_media = MediaQuery.of(context);
|
||||
_theme = Theme.of(context);
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -67,7 +64,7 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
showSnackBar(context, Text('Error: \n$e'));
|
||||
}
|
||||
},
|
||||
tooltip: 'convert',
|
||||
tooltip: s.convert,
|
||||
child: const Icon(Icons.send),
|
||||
),
|
||||
);
|
||||
@@ -85,7 +82,7 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
case 3:
|
||||
return Uri.decodeFull(text);
|
||||
default:
|
||||
return 'Unknown Convert Method';
|
||||
return s.unkownConvertMode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +94,14 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
}
|
||||
|
||||
Widget _buildTypeOption() {
|
||||
final decode = s.decode;
|
||||
final encode = s.encode;
|
||||
final List<String> _typeOption = [
|
||||
'Base64 $decode',
|
||||
'Base64 $encode',
|
||||
'URL $encode',
|
||||
'URL $decode'
|
||||
];
|
||||
return RoundRectCard(
|
||||
ExpansionTile(
|
||||
tilePadding: const EdgeInsets.only(left: 7, right: 27),
|
||||
@@ -106,7 +111,7 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
TextButton(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(primaryColor)),
|
||||
child: const Icon(Icons.change_circle),
|
||||
child: Icon(Icons.change_circle, semanticLabel: s.upsideDown),
|
||||
onPressed: () {
|
||||
final temp = _textEditingController.text;
|
||||
_textEditingController.text = _textEditingControllerResult.text;
|
||||
@@ -116,7 +121,7 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
TextButton(
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(primaryColor)),
|
||||
child: const Icon(Icons.copy),
|
||||
child: Icon(Icons.copy, semanticLabel: s.copy),
|
||||
onPressed: () => FlutterClipboard.copy(
|
||||
_textEditingControllerResult.text == ''
|
||||
? ' '
|
||||
@@ -137,11 +142,11 @@ class _ConvertPageState extends State<ConvertPage>
|
||||
fontSize: 16.0,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: primaryColor)),
|
||||
const Text(
|
||||
'Current Mode',
|
||||
Text(
|
||||
s.currentMode,
|
||||
textScaleFactor: 1.0,
|
||||
textAlign: TextAlign.right,
|
||||
style: TextStyle(fontSize: 9.0, color: Colors.grey),
|
||||
style: const TextStyle(fontSize: 9.0, color: Colors.grey),
|
||||
)
|
||||
],
|
||||
),
|
||||
|
||||
@@ -11,10 +11,12 @@ import 'package:toolbox/data/model/app/navigation_item.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/build_data.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/res/icon/common.dart';
|
||||
import 'package:toolbox/data/res/tab.dart';
|
||||
import 'package:toolbox/data/res/url.dart';
|
||||
import 'package:toolbox/data/store/setting.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/page/convert.dart';
|
||||
import 'package:toolbox/view/page/debug.dart';
|
||||
@@ -44,6 +46,7 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
late final AdvancedDrawerController _advancedDrawerController;
|
||||
late int _selectIndex;
|
||||
late double _width;
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -58,6 +61,7 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
_width = MediaQuery.of(context).size.width;
|
||||
}
|
||||
|
||||
@@ -115,11 +119,11 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
drawer: _buildDrawer(),
|
||||
child: Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(tabItems[_selectIndex].title),
|
||||
title: Text(tabTitleName(context, _selectIndex), style: size18),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.developer_mode, size: 23),
|
||||
tooltip: 'Debug',
|
||||
tooltip: s.debug,
|
||||
onPressed: () =>
|
||||
AppRoute(const DebugPage(), 'Debug Page').go(context),
|
||||
),
|
||||
@@ -171,7 +175,7 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
borderRadius: const BorderRadius.all(Radius.circular(50))),
|
||||
child: IconButton(
|
||||
icon: Icon(item.icon),
|
||||
tooltip: item.title,
|
||||
tooltip: tabTitleName(context, idx),
|
||||
splashRadius: width / 3.3,
|
||||
padding: const EdgeInsets.only(left: 17, right: 17),
|
||||
onPressed: () {
|
||||
@@ -218,36 +222,35 @@ class _MyHomePageState extends State<MyHomePage>
|
||||
children: [
|
||||
ListTile(
|
||||
leading: const Icon(Icons.settings),
|
||||
title: const Text('Setting'),
|
||||
title: Text(s.setting),
|
||||
onTap: () =>
|
||||
AppRoute(const SettingPage(), 'Setting').go(context),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.vpn_key),
|
||||
title: const Text('Private Key'),
|
||||
title: Text(s.privateKey),
|
||||
onTap: () => AppRoute(
|
||||
const StoredPrivateKeysPage(), 'private key list')
|
||||
.go(context),
|
||||
),
|
||||
ListTile(
|
||||
leading: const Icon(Icons.snippet_folder),
|
||||
title: const Text('Snippet'),
|
||||
title: Text(s.snippet),
|
||||
onTap: () => AppRoute(const SnippetListPage(), 'snippet list')
|
||||
.go(context),
|
||||
),
|
||||
AboutListTile(
|
||||
icon: const Icon(Icons.text_snippet),
|
||||
child: const Text('Licences'),
|
||||
child: Text(s.license),
|
||||
applicationName: BuildData.name,
|
||||
applicationVersion: _buildVersionStr(),
|
||||
applicationIcon: _buildIcon(),
|
||||
aboutBoxChildren: const [
|
||||
aboutBoxChildren: [
|
||||
UrlText(
|
||||
text: '\nMade with ❤️ by $myGithub',
|
||||
text: s.madeWithLove(myGithub),
|
||||
replace: 'LollipopKit'),
|
||||
UrlText(
|
||||
text:
|
||||
'\nThanks $rainSunMeGithub for participating in the test.\n\nAll rights reserved.',
|
||||
text: s.aboutThanks(rainSunMeGithub),
|
||||
replace: 'RainSunMe',
|
||||
),
|
||||
],
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:toolbox/core/utils.dart';
|
||||
import 'package:toolbox/data/model/server/ping_result.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/widget/input_field.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
@@ -20,6 +21,10 @@ class _PingPageState extends State<PingPage>
|
||||
late TextEditingController _textEditingController;
|
||||
late MediaQueryData _media;
|
||||
final List<PingResult> _results = [];
|
||||
late S s;
|
||||
static const summaryTextStyle = TextStyle(
|
||||
fontSize: 12,
|
||||
);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -31,6 +36,7 @@ class _PingPageState extends State<PingPage>
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
_media = MediaQuery.of(context);
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -62,37 +68,39 @@ class _PingPageState extends State<PingPage>
|
||||
}
|
||||
|
||||
Widget _buildResultItem(PingResult result) {
|
||||
final unknown = s.unknown;
|
||||
final ms = s.ms;
|
||||
return RoundRectCard(ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
||||
title: Text(result.serverName,
|
||||
style: TextStyle(
|
||||
fontSize: 18, fontWeight: FontWeight.bold, color: primaryColor)),
|
||||
subtitle: Text(_buildPingSummary(result)),
|
||||
subtitle: Text(_buildPingSummary(result, unknown, ms), style: summaryTextStyle,),
|
||||
trailing: Text(
|
||||
'Avg: ' +
|
||||
(result.statistic?.avg?.toStringAsFixed(2) ?? 'unkown') +
|
||||
' ms',
|
||||
s.pingAvg +
|
||||
(result.statistic?.avg?.toStringAsFixed(2) ?? s.unknown) +
|
||||
' $ms',
|
||||
style: TextStyle(fontSize: 14, color: primaryColor)),
|
||||
));
|
||||
}
|
||||
|
||||
String _buildPingSummary(PingResult result) {
|
||||
final ip = result.ip ?? 'unkown';
|
||||
String _buildPingSummary(PingResult result, String unknown, String ms) {
|
||||
final ip = result.ip ?? unknown;
|
||||
if (result.results == null || result.results!.isEmpty) {
|
||||
return '$ip - no results';
|
||||
return '$ip - ${s.noResult}';
|
||||
}
|
||||
final ttl = result.results?.first.ttl ?? 'unkown';
|
||||
final loss = result.statistic?.loss ?? 'unkown';
|
||||
final min = result.statistic?.min ?? 'unkown';
|
||||
final max = result.statistic?.max ?? 'unkown';
|
||||
return '$ip\nttl: $ttl, loss: $loss%\nmin: $min ms, max: $max ms';
|
||||
final ttl = result.results?.first.ttl ?? unknown;
|
||||
final loss = result.statistic?.loss ?? unknown;
|
||||
final min = result.statistic?.min ?? unknown;
|
||||
final max = result.statistic?.max ?? unknown;
|
||||
return '$ip\n${s.ttl}: $ttl, ${s.loss}: $loss%\n${s.min}: $min $ms, ${s.max}: $max $ms';
|
||||
}
|
||||
|
||||
Future<void> doPing() async {
|
||||
_results.clear();
|
||||
final target = _textEditingController.text.trim();
|
||||
if (target.isEmpty) {
|
||||
showSnackBar(context, const Text('Please input a target'));
|
||||
showSnackBar(context, Text(s.pingInputIP));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -119,12 +127,12 @@ class _PingPageState extends State<PingPage>
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(primaryColor)),
|
||||
child: Row(
|
||||
children: const [
|
||||
Icon(Icons.delete),
|
||||
SizedBox(
|
||||
children: [
|
||||
const Icon(Icons.delete),
|
||||
const SizedBox(
|
||||
width: 7,
|
||||
),
|
||||
Text('Clear')
|
||||
Text(s.clear)
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
@@ -136,12 +144,12 @@ class _PingPageState extends State<PingPage>
|
||||
style: ButtonStyle(
|
||||
foregroundColor: MaterialStateProperty.all(primaryColor)),
|
||||
child: Row(
|
||||
children: const [
|
||||
Icon(Icons.play_arrow),
|
||||
SizedBox(
|
||||
children: [
|
||||
const Icon(Icons.play_arrow),
|
||||
const SizedBox(
|
||||
width: 7,
|
||||
),
|
||||
Text('Start')
|
||||
Text(s.start)
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
|
||||
@@ -5,6 +5,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:toolbox/core/utils.dart';
|
||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
||||
import 'package:toolbox/data/provider/private_key.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
||||
|
||||
@@ -25,6 +27,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
||||
|
||||
late PrivateKeyProvider _provider;
|
||||
late Widget loading;
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -33,12 +36,19 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
||||
loading = const SizedBox();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Edit'), actions: [
|
||||
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
|
||||
widget.info != null
|
||||
? IconButton(
|
||||
tooltip: s.delete,
|
||||
onPressed: () {
|
||||
_provider.delInfo(widget.info!);
|
||||
Navigator.of(context).pop();
|
||||
@@ -52,7 +62,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
||||
TextField(
|
||||
controller: nameController,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: buildDecoration('Name', icon: Icons.info),
|
||||
decoration: buildDecoration(s.name, icon: Icons.info),
|
||||
),
|
||||
TextField(
|
||||
controller: keyController,
|
||||
@@ -61,14 +71,14 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
||||
maxLines: 10,
|
||||
keyboardType: TextInputType.text,
|
||||
enableSuggestions: false,
|
||||
decoration: buildDecoration('Private Key', icon: Icons.vpn_key),
|
||||
decoration: buildDecoration(s.privateKey, icon: Icons.vpn_key),
|
||||
),
|
||||
TextField(
|
||||
controller: pwdController,
|
||||
autocorrect: false,
|
||||
keyboardType: TextInputType.text,
|
||||
obscureText: true,
|
||||
decoration: buildDecoration('Password', icon: Icons.password),
|
||||
decoration: buildDecoration(s.pwd, icon: Icons.password),
|
||||
),
|
||||
SizedBox(height: MediaQuery.of(context).size.height * 0.1),
|
||||
loading
|
||||
@@ -76,13 +86,14 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage>
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
child: const Icon(Icons.save),
|
||||
tooltip: s.save,
|
||||
onPressed: () async {
|
||||
final name = nameController.text;
|
||||
final key = keyController.text;
|
||||
final pwd = pwdController.text;
|
||||
if (name.isEmpty || key.isEmpty) {
|
||||
showSnackBar(
|
||||
context, const Text('Name and Key must not be empty.'));
|
||||
context, Text(s.fieldMustNotEmpty));
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).unfocus();
|
||||
|
||||
@@ -3,7 +3,9 @@ import 'package:provider/provider.dart';
|
||||
import 'package:toolbox/core/route.dart';
|
||||
import 'package:toolbox/data/provider/private_key.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/res/padding.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/view/page/private_key/edit.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
|
||||
@@ -16,11 +18,19 @@ class StoredPrivateKeysPage extends StatefulWidget {
|
||||
|
||||
class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
|
||||
final _textStyle = TextStyle(color: primaryColor);
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Private Keys'),
|
||||
title: Text(s.privateKey, style: size18),
|
||||
),
|
||||
body: Consumer<PrivateKeyProvider>(
|
||||
builder: (_, key, __) {
|
||||
@@ -46,14 +56,14 @@ class _PrivateKeyListState extends State<StoredPrivateKeysPage> {
|
||||
'private key edit page')
|
||||
.go(context),
|
||||
child: Text(
|
||||
'Edit',
|
||||
s.edit,
|
||||
style: _textStyle,
|
||||
))
|
||||
],
|
||||
),
|
||||
));
|
||||
})
|
||||
: const Center(child: Text('No saved private keys.'));
|
||||
: Center(child: Text(s.noSavedPrivateKey));
|
||||
},
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:toolbox/data/model/server/server.dart';
|
||||
import 'package:toolbox/data/model/server/server_status.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/res/icon/linux_icons.dart';
|
||||
import 'package:toolbox/data/res/padding.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
@@ -43,7 +44,7 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
||||
Widget _buildMainPage(ServerInfo si) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(si.info.name),
|
||||
title: Text(si.info.name, style: size18),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(13),
|
||||
|
||||
@@ -8,7 +8,9 @@ import 'package:toolbox/data/model/server/server_private_info.dart';
|
||||
import 'package:toolbox/data/provider/private_key.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/store/private_key.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/page/private_key/edit.dart';
|
||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
||||
@@ -32,6 +34,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
|
||||
late ServerProvider _serverProvider;
|
||||
|
||||
late S s;
|
||||
|
||||
bool usePublicKey = false;
|
||||
|
||||
int _pubKeyIndex = -1;
|
||||
@@ -43,10 +47,16 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
_serverProvider = locator<ServerProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Edit'), actions: [
|
||||
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
|
||||
widget.spi != null
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
@@ -54,7 +64,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
context,
|
||||
'Attention',
|
||||
Text(
|
||||
'Are you sure to delete server [${widget.spi!.name}]'),
|
||||
s.sureToDeleteServer(widget.spi!.name)),
|
||||
[
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
@@ -62,13 +72,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
Navigator.of(context).pop();
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: const Text(
|
||||
'Yes',
|
||||
style: TextStyle(color: Colors.red),
|
||||
child: Text(
|
||||
s.ok,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
)),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('No'))
|
||||
child: Text(s.cancel))
|
||||
]);
|
||||
},
|
||||
icon: const Icon(Icons.delete))
|
||||
@@ -84,20 +94,20 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
controller: nameController,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration:
|
||||
buildDecoration('Name', icon: Icons.info, hint: 'Example'),
|
||||
buildDecoration(s.name, icon: Icons.info, hint: s.exampleName),
|
||||
),
|
||||
TextField(
|
||||
controller: ipController,
|
||||
keyboardType: TextInputType.text,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
decoration: buildDecoration('Host',
|
||||
decoration: buildDecoration(s.host,
|
||||
icon: Icons.storage, hint: 'example.com'),
|
||||
),
|
||||
TextField(
|
||||
controller: portController,
|
||||
keyboardType: TextInputType.number,
|
||||
decoration: buildDecoration('Port',
|
||||
decoration: buildDecoration(s.port,
|
||||
icon: Icons.format_list_numbered, hint: '22'),
|
||||
),
|
||||
TextField(
|
||||
@@ -105,13 +115,13 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
keyboardType: TextInputType.text,
|
||||
autocorrect: false,
|
||||
enableSuggestions: false,
|
||||
decoration: buildDecoration('User',
|
||||
decoration: buildDecoration(s.user,
|
||||
icon: Icons.account_box, hint: 'root'),
|
||||
),
|
||||
const SizedBox(height: 7),
|
||||
Row(
|
||||
children: [
|
||||
const Text('Key Auth'),
|
||||
Text(s.keyAuth),
|
||||
Switch(
|
||||
value: usePublicKey,
|
||||
onChanged: (val) => setState(() => usePublicKey = val)),
|
||||
@@ -122,8 +132,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
controller: passwordController,
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: buildDecoration('Pwd',
|
||||
icon: Icons.password, hint: 'Password'),
|
||||
decoration: buildDecoration(s.pwd,
|
||||
icon: Icons.password, hint: s.pwd),
|
||||
onSubmitted: (_) => {},
|
||||
)
|
||||
: const SizedBox(),
|
||||
@@ -143,7 +153,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
)
|
||||
.toList();
|
||||
tiles.add(ListTile(
|
||||
title: const Text('Add a Private Key'),
|
||||
title: Text(s.addPrivateKey),
|
||||
contentPadding: EdgeInsets.zero,
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.add),
|
||||
@@ -157,9 +167,9 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
iconColor: primaryColor,
|
||||
tilePadding: EdgeInsets.zero,
|
||||
childrenPadding: EdgeInsets.zero,
|
||||
title: const Text(
|
||||
'Choose Key',
|
||||
style: TextStyle(fontSize: 14),
|
||||
title: Text(
|
||||
s.choosePrivateKey,
|
||||
style: const TextStyle(fontSize: 14),
|
||||
),
|
||||
children: tiles,
|
||||
);
|
||||
@@ -172,15 +182,15 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
child: const Icon(Icons.send),
|
||||
onPressed: () {
|
||||
if (ipController.text == '') {
|
||||
showSnackBar(context, const Text('Please enter host.'));
|
||||
showSnackBar(context, Text(s.plzEnterHost));
|
||||
return;
|
||||
}
|
||||
if (!usePublicKey && passwordController.text == '') {
|
||||
showSnackBar(context, const Text('Please enter password.'));
|
||||
showSnackBar(context, Text(s.plzEnterPwd));
|
||||
return;
|
||||
}
|
||||
if (usePublicKey && _pubKeyIndex == -1) {
|
||||
showSnackBar(context, const Text('Please select a private key.'));
|
||||
showSnackBar(context, Text(s.plzSelectKey));
|
||||
return;
|
||||
}
|
||||
if (usernameController.text == '') {
|
||||
|
||||
@@ -15,6 +15,7 @@ import 'package:toolbox/data/model/server/server_status.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/store/setting.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/page/apt.dart';
|
||||
import 'package:toolbox/view/page/docker.dart';
|
||||
@@ -39,6 +40,7 @@ class _ServerPageState extends State<ServerPage>
|
||||
late RefreshController _refreshController;
|
||||
|
||||
late ServerProvider _serverProvider;
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -53,6 +55,7 @@ class _ServerPageState extends State<ServerPage>
|
||||
_media = MediaQuery.of(context);
|
||||
_theme = Theme.of(context);
|
||||
_primaryColor = primaryColor;
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -62,9 +65,9 @@ class _ServerPageState extends State<ServerPage>
|
||||
locator<SettingStore>().serverStatusUpdateInterval.fetch() != 0;
|
||||
final child = Consumer<ServerProvider>(builder: (_, pro, __) {
|
||||
if (pro.servers.isEmpty) {
|
||||
return const Center(
|
||||
return Center(
|
||||
child: Text(
|
||||
'There is no server.\nClick the fab to add one.',
|
||||
s.serverTabEmpty,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
);
|
||||
@@ -99,7 +102,7 @@ class _ServerPageState extends State<ServerPage>
|
||||
onPressed: () =>
|
||||
AppRoute(const ServerEditPage(), 'Add server info page')
|
||||
.go(context),
|
||||
tooltip: 'add a server',
|
||||
tooltip: s.addAServer,
|
||||
heroTag: 'server page fab',
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
@@ -302,7 +305,7 @@ class _ServerPageState extends State<ServerPage>
|
||||
case ServerConnectionState.connected:
|
||||
if (temp == '') {
|
||||
if (upTime == '') {
|
||||
return 'Loading...';
|
||||
return s.serverTabLoading;
|
||||
} else {
|
||||
return upTime;
|
||||
}
|
||||
@@ -314,17 +317,17 @@ class _ServerPageState extends State<ServerPage>
|
||||
}
|
||||
}
|
||||
case ServerConnectionState.connecting:
|
||||
return 'Connecting...';
|
||||
return s.serverTabConnecting;
|
||||
case ServerConnectionState.failed:
|
||||
if (failedInfo == null) {
|
||||
return 'Failed';
|
||||
return s.serverTabFailed;
|
||||
}
|
||||
if (failedInfo.contains('encypted')) {
|
||||
return 'Please "save" this private key again.';
|
||||
return s.serverTabPlzSave;
|
||||
}
|
||||
return failedInfo;
|
||||
default:
|
||||
return 'Unknown State';
|
||||
return s.serverTabUnkown;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,13 +2,16 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:toolbox/core/update.dart';
|
||||
import 'package:toolbox/core/utils.dart';
|
||||
import 'package:toolbox/data/provider/app.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/res/build_data.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/res/padding.dart';
|
||||
import 'package:toolbox/data/res/tab.dart';
|
||||
import 'package:toolbox/data/store/setting.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
|
||||
@@ -29,6 +32,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
late final ServerProvider _serverProvider;
|
||||
late MediaQueryData _media;
|
||||
late ThemeData _theme;
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
@@ -36,6 +40,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
priColor = primaryColor;
|
||||
_media = MediaQuery.of(context);
|
||||
_theme = Theme.of(context);
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -51,7 +56,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Setting'),
|
||||
title: Text(s.setting, style: size18),
|
||||
),
|
||||
body: ListView(
|
||||
padding: const EdgeInsets.all(17),
|
||||
@@ -70,12 +75,12 @@ class _SettingPageState extends State<SettingPage> {
|
||||
String display;
|
||||
if (app.newestBuild != null) {
|
||||
if (app.newestBuild! > BuildData.build) {
|
||||
display = 'Found: v1.0.${app.newestBuild}, click to update';
|
||||
display = s.versionHaveUpdate(app.newestBuild!);
|
||||
} else {
|
||||
display = 'Current: v1.0.${BuildData.build},is up to date';
|
||||
display = s.versionUpdated(BuildData.build);
|
||||
}
|
||||
} else {
|
||||
display = 'Current: v1.0.${BuildData.build}';
|
||||
display = s.versionUnknownUpdate(BuildData.build);
|
||||
}
|
||||
return ListTile(
|
||||
contentPadding: roundRectCardPadding,
|
||||
@@ -94,16 +99,16 @@ class _SettingPageState extends State<SettingPage> {
|
||||
tilePadding: roundRectCardPadding,
|
||||
childrenPadding: roundRectCardPadding,
|
||||
textColor: priColor,
|
||||
title: const Text(
|
||||
'Server status update interval',
|
||||
title: Text(
|
||||
s.updateServerStatusInterval,
|
||||
style: textStyle,
|
||||
textAlign: TextAlign.start,
|
||||
),
|
||||
subtitle: const Text(
|
||||
'Will take effect immediately.',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
subtitle: Text(
|
||||
s.willTakEeffectImmediately,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 13),
|
||||
),
|
||||
trailing: Text('${_intervalValue.toInt()} s'),
|
||||
trailing: Text('${_intervalValue.toInt()} ${s.second}'),
|
||||
children: [
|
||||
Slider(
|
||||
thumbColor: priColor,
|
||||
@@ -120,16 +125,16 @@ class _SettingPageState extends State<SettingPage> {
|
||||
_store.serverStatusUpdateInterval.put(val.toInt());
|
||||
_serverProvider.startAutoRefresh();
|
||||
},
|
||||
label: '${_intervalValue.toInt()} seconds',
|
||||
label: '${_intervalValue.toInt()} ${s.second}',
|
||||
divisions: 10,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 3,
|
||||
),
|
||||
_intervalValue == 0.0
|
||||
? const Text(
|
||||
'You set to 0, will not update automatically.\nYou can pull to refresh manually.',
|
||||
style: TextStyle(color: Colors.grey),
|
||||
? Text(
|
||||
s.updateIntervalEqual0,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12),
|
||||
textAlign: TextAlign.center,
|
||||
)
|
||||
: const SizedBox(),
|
||||
@@ -156,8 +161,8 @@ class _SettingPageState extends State<SettingPage> {
|
||||
width: 27,
|
||||
),
|
||||
),
|
||||
title: const Text(
|
||||
'App primary color',
|
||||
title: Text(
|
||||
s.appPrimaryColor,
|
||||
style: textStyle,
|
||||
));
|
||||
}
|
||||
@@ -186,14 +191,14 @@ class _SettingPageState extends State<SettingPage> {
|
||||
textColor: priColor,
|
||||
tilePadding: roundRectCardPadding,
|
||||
childrenPadding: roundRectCardPadding,
|
||||
title: const Text(
|
||||
'Launch page',
|
||||
title: Text(
|
||||
s.launchPage,
|
||||
style: textStyle,
|
||||
),
|
||||
trailing: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxWidth: _media.size.width * 0.35),
|
||||
child: Text(
|
||||
tabs[_launchPageIdx],
|
||||
tabTitleName(context, _launchPageIdx),
|
||||
style: textStyle,
|
||||
textAlign: TextAlign.right,
|
||||
),
|
||||
@@ -202,7 +207,7 @@ class _SettingPageState extends State<SettingPage> {
|
||||
.map((e) => ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
title: Text(
|
||||
e,
|
||||
tabTitleName(context, tabs.indexOf(e)),
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: _theme.textTheme.bodyText2!.color!.withAlpha(177)),
|
||||
|
||||
@@ -3,6 +3,8 @@ import 'package:flutter/material.dart';
|
||||
import 'package:toolbox/core/utils.dart';
|
||||
import 'package:toolbox/data/model/server/snippet.dart';
|
||||
import 'package:toolbox/data/provider/snippet.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
||||
|
||||
@@ -21,23 +23,31 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
final scriptController = TextEditingController();
|
||||
|
||||
late SnippetProvider _provider;
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_provider = locator<SnippetProvider>();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('Edit'), actions: [
|
||||
appBar: AppBar(title: Text(s.edit, style: size18), actions: [
|
||||
widget.snippet != null
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
_provider.del(widget.snippet!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
tooltip: s.delete,
|
||||
icon: const Icon(Icons.delete))
|
||||
: const SizedBox()
|
||||
]),
|
||||
@@ -47,7 +57,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
TextField(
|
||||
controller: nameController,
|
||||
keyboardType: TextInputType.text,
|
||||
decoration: buildDecoration('Name', icon: Icons.info),
|
||||
decoration: buildDecoration(s.name, icon: Icons.info),
|
||||
),
|
||||
TextField(
|
||||
controller: scriptController,
|
||||
@@ -56,7 +66,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
maxLines: 10,
|
||||
keyboardType: TextInputType.text,
|
||||
enableSuggestions: false,
|
||||
decoration: buildDecoration('Snippet', icon: Icons.code),
|
||||
decoration: buildDecoration(s.snippet, icon: Icons.code),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -66,7 +76,7 @@ class _SnippetEditPageState extends State<SnippetEditPage>
|
||||
final name = nameController.text;
|
||||
final script = scriptController.text;
|
||||
if (name.isEmpty || script.isEmpty) {
|
||||
showSnackBar(context, const Text('Two fields must not be empty.'));
|
||||
showSnackBar(context, Text(s.fieldMustNotEmpty));
|
||||
return;
|
||||
}
|
||||
final snippet = Snippet(name, script);
|
||||
|
||||
@@ -8,7 +8,9 @@ import 'package:toolbox/data/model/server/snippet.dart';
|
||||
import 'package:toolbox/data/provider/server.dart';
|
||||
import 'package:toolbox/data/provider/snippet.dart';
|
||||
import 'package:toolbox/data/res/color.dart';
|
||||
import 'package:toolbox/data/res/font_style.dart';
|
||||
import 'package:toolbox/data/res/padding.dart';
|
||||
import 'package:toolbox/generated/l10n.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
import 'package:toolbox/view/page/snippet/edit.dart';
|
||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
||||
@@ -28,14 +30,24 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
final _exportFieldController = TextEditingController();
|
||||
|
||||
final _textStyle = TextStyle(color: primaryColor);
|
||||
|
||||
late S s;
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
s = S.of(context);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Snippet List'),
|
||||
title: Text(s.snippet, style: size18),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => _showImportExport(),
|
||||
tooltip: s.importAndExport,
|
||||
icon: const Icon(Icons.import_export)),
|
||||
],
|
||||
),
|
||||
@@ -51,17 +63,17 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
Future<void> _showImportExport() async {
|
||||
await showRoundDialog(
|
||||
context,
|
||||
'Choose',
|
||||
s.choose,
|
||||
Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: const Text('Import'),
|
||||
title: Text(s.import),
|
||||
leading: const Icon(Icons.download),
|
||||
onTap: () => _showImportDialog(),
|
||||
),
|
||||
ListTile(
|
||||
title: const Text('Export'),
|
||||
title: Text(s.export),
|
||||
leading: const Icon(Icons.file_upload),
|
||||
onTap: () => _showExportDialog(),
|
||||
),
|
||||
@@ -75,7 +87,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
_exportFieldController.text = locator<SnippetProvider>().export;
|
||||
await showRoundDialog(
|
||||
context,
|
||||
'Export',
|
||||
s.export,
|
||||
TextField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'JSON',
|
||||
@@ -85,7 +97,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
),
|
||||
[
|
||||
TextButton(
|
||||
child: const Text('OK'),
|
||||
child: Text(s.ok),
|
||||
onPressed: () => Navigator.pop(context),
|
||||
),
|
||||
]);
|
||||
@@ -95,10 +107,10 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
Navigator.of(context).pop();
|
||||
await showRoundDialog(
|
||||
context,
|
||||
'Import',
|
||||
s.import,
|
||||
TextField(
|
||||
decoration: const InputDecoration(
|
||||
labelText: 'Url or JSON',
|
||||
decoration: InputDecoration(
|
||||
labelText: s.urlOrJson,
|
||||
),
|
||||
maxLines: 2,
|
||||
controller: _importFieldController,
|
||||
@@ -106,18 +118,18 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel')),
|
||||
child: Text(s.cancel)),
|
||||
TextButton(
|
||||
onPressed: () async =>
|
||||
await _import(_importFieldController.text.trim()),
|
||||
child: const Text('GO'),
|
||||
child: Text('GO'),
|
||||
)
|
||||
]);
|
||||
}
|
||||
|
||||
Future<void> _import(String text) async {
|
||||
if (text.isEmpty) {
|
||||
showSnackBar(context, const Text('field can not be empty'));
|
||||
showSnackBar(context, Text(s.fieldMustNotEmpty));
|
||||
return;
|
||||
}
|
||||
final snippetProvider = locator<SnippetProvider>();
|
||||
@@ -125,7 +137,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
final resp = await Dio().get(text);
|
||||
if (resp.statusCode != 200) {
|
||||
showSnackBar(
|
||||
context, Text('request failed, status code: ${resp.statusCode}'));
|
||||
context, Text(s.httpFailedWithCode(resp.statusCode ?? '-1')));
|
||||
return;
|
||||
}
|
||||
for (final snippet in getSnippetList(resp.data)) {
|
||||
@@ -166,7 +178,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
'snippet edit page')
|
||||
.go(context),
|
||||
child: Text(
|
||||
'Edit',
|
||||
s.edit,
|
||||
style: _textStyle,
|
||||
)),
|
||||
TextButton(
|
||||
@@ -179,7 +191,7 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
run(context, snippet);
|
||||
},
|
||||
child: Text(
|
||||
'Run',
|
||||
s.run,
|
||||
style: _textStyle,
|
||||
))
|
||||
])
|
||||
@@ -187,16 +199,16 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
),
|
||||
));
|
||||
})
|
||||
: const Center(child: Text('No saved snippets.'));
|
||||
: Center(child: Text(s.noSavedSnippet));
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showRunDialog(Snippet snippet) {
|
||||
showRoundDialog(context, 'Choose destination',
|
||||
showRoundDialog(context, s.chooseDestination,
|
||||
Consumer<ServerProvider>(builder: (_, provider, __) {
|
||||
if (provider.servers.isEmpty) {
|
||||
return const Text('No server available');
|
||||
return Text(s.noServerAvailable);
|
||||
}
|
||||
_selectedIndex = provider.servers.first.info;
|
||||
return SizedBox(
|
||||
@@ -235,10 +247,10 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
}), [
|
||||
TextButton(
|
||||
onPressed: () async => run(context, snippet),
|
||||
child: const Text('Run')),
|
||||
child: Text(s.run)),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Cancel')),
|
||||
child: Text(s.cancel)),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -246,11 +258,11 @@ class _SnippetListPageState extends State<SnippetListPage> {
|
||||
final result = await locator<ServerProvider>()
|
||||
.runSnippet(widget.spi ?? _selectedIndex, snippet);
|
||||
if (result != null) {
|
||||
showRoundDialog(context, 'Result',
|
||||
showRoundDialog(context, s.result,
|
||||
Text(result, style: const TextStyle(fontSize: 13)), [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
child: const Text('Close'))
|
||||
child: Text(s.close))
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user