mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 15:24:35 +01:00
fix & opt
fix: android in-app upgrade fmt: proj struct opt: fetch primaryColor
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:toolbox/core/extension/colorx.dart';
|
import 'package:toolbox/core/extension/colorx.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/res/build_data.dart';
|
import 'package:toolbox/data/res/build_data.dart';
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
import 'package:toolbox/data/store/setting.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import 'package:toolbox/generated/l10n.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import 'package:toolbox/locator.dart';
|
||||||
import 'package:toolbox/view/page/home.dart';
|
import 'package:toolbox/view/page/home.dart';
|
||||||
|
|
||||||
|
import 'core/utils/ui.dart';
|
||||||
|
|
||||||
class MyApp extends StatelessWidget {
|
class MyApp extends StatelessWidget {
|
||||||
const MyApp({Key? key}) : super(key: key);
|
const MyApp({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import '../data/res/build_data.dart';
|
|||||||
import '../data/service/app.dart';
|
import '../data/service/app.dart';
|
||||||
import '../generated/l10n.dart';
|
import '../generated/l10n.dart';
|
||||||
import '../locator.dart';
|
import '../locator.dart';
|
||||||
import 'utils.dart';
|
import 'utils/ui.dart';
|
||||||
|
|
||||||
final _logger = Logger('UPDATE');
|
final _logger = Logger('UPDATE');
|
||||||
|
|
||||||
@@ -74,7 +74,7 @@ Future<void> doUpdate(BuildContext context, {bool force = false}) async {
|
|||||||
Future<void> _doUpdate(AppUpdate update, BuildContext context, S s) async {
|
Future<void> _doUpdate(AppUpdate update, BuildContext context, S s) async {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
await RUpgrade.upgrade(update.android,
|
await RUpgrade.upgrade(update.android,
|
||||||
fileName: update.android.split('/').last);
|
fileName: update.android.split('/').last, isAutoRequestInstall: true);
|
||||||
} else if (Platform.isIOS) {
|
} else if (Platform.isIOS) {
|
||||||
await RUpgrade.upgradeFromAppStore('1586449703');
|
await RUpgrade.upgradeFromAppStore('1586449703');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
23
lib/core/utils/misc.dart
Normal file
23
lib/core/utils/misc.dart
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
import 'package:share_plus/share_plus.dart';
|
||||||
|
|
||||||
|
import '../../generated/l10n.dart';
|
||||||
|
|
||||||
|
Future<bool> shareFiles(BuildContext context, List<String> filePaths) async {
|
||||||
|
for (final filePath in filePaths) {
|
||||||
|
if (!await File(filePath).exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var text = '';
|
||||||
|
if (filePaths.length == 1) {
|
||||||
|
text = filePaths.first.split('/').last;
|
||||||
|
} else {
|
||||||
|
text = '${filePaths.length} ${S.of(context).files}';
|
||||||
|
}
|
||||||
|
final xfiles = filePaths.map((e) => XFile(e)).toList();
|
||||||
|
await Share.shareXFiles(xfiles, text: 'ServerBox -> $text');
|
||||||
|
return filePaths.isNotEmpty;
|
||||||
|
}
|
||||||
32
lib/core/utils/server.dart
Normal file
32
lib/core/utils/server.dart
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:dartssh2/dartssh2.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
import '../../data/model/server/server_private_info.dart';
|
||||||
|
import '../../data/store/private_key.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
|
||||||
|
/// Must put this func out of any Class.
|
||||||
|
/// Because of this function is called by [compute] in [ServerProvider.genClient].
|
||||||
|
/// https://stackoverflow.com/questions/51998995/invalid-arguments-illegal-argument-in-isolate-message-object-is-a-closure
|
||||||
|
List<SSHKeyPair> loadIndentity(String key) {
|
||||||
|
return SSHKeyPair.fromPem(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<SSHClient> genClient(ServerPrivateInfo spi) async {
|
||||||
|
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
||||||
|
if (spi.pubKeyId == null) {
|
||||||
|
return SSHClient(
|
||||||
|
socket,
|
||||||
|
username: spi.user,
|
||||||
|
onPasswordRequest: () => spi.pwd,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
final key = locator<PrivateKeyStore>().get(spi.pubKeyId!);
|
||||||
|
return SSHClient(
|
||||||
|
socket,
|
||||||
|
username: spi.user,
|
||||||
|
identities: await compute(loadIndentity, key.privateKey),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,22 +1,13 @@
|
|||||||
import 'dart:async';
|
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:share_plus/share_plus.dart';
|
|
||||||
import 'package:toolbox/core/persistant_store.dart';
|
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
|
||||||
import 'package:toolbox/view/widget/card_dialog.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
import 'package:toolbox/core/extension/stringx.dart';
|
import 'package:toolbox/core/extension/stringx.dart';
|
||||||
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
/// Must put this func out of any Class.
|
import '../../generated/l10n.dart';
|
||||||
/// Because of this function is called by [compute] in [ServerProvider.genClient].
|
import '../../view/widget/card_dialog.dart';
|
||||||
/// https://stackoverflow.com/questions/51998995/invalid-arguments-illegal-argument-in-isolate-message-object-is-a-closure
|
import '../persistant_store.dart';
|
||||||
List<SSHKeyPair> loadIndentity(String key) {
|
|
||||||
return SSHKeyPair.fromPem(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isDarkMode(BuildContext context) =>
|
bool isDarkMode(BuildContext context) =>
|
||||||
Theme.of(context).brightness == Brightness.dark;
|
Theme.of(context).brightness == Brightness.dark;
|
||||||
@@ -79,37 +70,6 @@ void setTransparentNavigationBar(BuildContext context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
String tabTitleName(BuildContext context, int i) {
|
|
||||||
final s = S.of(context);
|
|
||||||
switch (i) {
|
|
||||||
case 0:
|
|
||||||
return s.server;
|
|
||||||
case 1:
|
|
||||||
return s.convert;
|
|
||||||
case 2:
|
|
||||||
return s.ping;
|
|
||||||
default:
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<bool> shareFiles(BuildContext context, List<String> filePaths) async {
|
|
||||||
for (final filePath in filePaths) {
|
|
||||||
if (!await File(filePath).exists()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var text = '';
|
|
||||||
if (filePaths.length == 1) {
|
|
||||||
text = filePaths.first.split('/').last;
|
|
||||||
} else {
|
|
||||||
text = '${filePaths.length} ${S.of(context).files}';
|
|
||||||
}
|
|
||||||
final xfiles = filePaths.map((e) => XFile(e)).toList();
|
|
||||||
await Share.shareXFiles(xfiles, text: 'ServerBox -> $text');
|
|
||||||
return filePaths.isNotEmpty;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget buildPopuopMenu(
|
Widget buildPopuopMenu(
|
||||||
{required List<PopupMenuEntry> items,
|
{required List<PopupMenuEntry> items,
|
||||||
required Function(dynamic) onSelected}) {
|
required Function(dynamic) onSelected}) {
|
||||||
@@ -127,3 +87,17 @@ Widget buildPopuopMenu(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String tabTitleName(BuildContext context, int i) {
|
||||||
|
final s = S.of(context);
|
||||||
|
switch (i) {
|
||||||
|
case 0:
|
||||||
|
return s.server;
|
||||||
|
case 1:
|
||||||
|
return s.convert;
|
||||||
|
case 2:
|
||||||
|
return s.ping;
|
||||||
|
default:
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,12 +1,11 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:dartssh2/dartssh2.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
|
|
||||||
import '../../core/extension/uint8list.dart';
|
import '../../core/extension/uint8list.dart';
|
||||||
import '../../core/provider_base.dart';
|
import '../../core/provider_base.dart';
|
||||||
import '../../core/utils.dart';
|
import '../../core/utils/server.dart';
|
||||||
import '../../locator.dart';
|
import '../../locator.dart';
|
||||||
import '../model/server/cpu_status.dart';
|
import '../model/server/cpu_status.dart';
|
||||||
import '../model/server/disk_info.dart';
|
import '../model/server/disk_info.dart';
|
||||||
@@ -16,24 +15,11 @@ import '../model/server/server.dart';
|
|||||||
import '../model/server/server_private_info.dart';
|
import '../model/server/server_private_info.dart';
|
||||||
import '../model/server/snippet.dart';
|
import '../model/server/snippet.dart';
|
||||||
import '../model/server/tcp_status.dart';
|
import '../model/server/tcp_status.dart';
|
||||||
|
import '../res/server_cmd.dart';
|
||||||
import '../res/status.dart';
|
import '../res/status.dart';
|
||||||
import '../store/private_key.dart';
|
|
||||||
import '../store/server.dart';
|
import '../store/server.dart';
|
||||||
import '../store/setting.dart';
|
import '../store/setting.dart';
|
||||||
|
|
||||||
const seperator = 'A====A';
|
|
||||||
const shellCmd = "export LANG=en_US.utf-8 \necho '$seperator' \n"
|
|
||||||
"cat /proc/net/dev && date +%s \necho $seperator \n "
|
|
||||||
"cat /etc/os-release | grep PRETTY_NAME \necho $seperator \n"
|
|
||||||
"cat /proc/stat | grep cpu \necho $seperator \n"
|
|
||||||
"uptime \necho $seperator \n"
|
|
||||||
"cat /proc/net/snmp \necho $seperator \n"
|
|
||||||
"df -h \necho $seperator \n"
|
|
||||||
"cat /proc/meminfo \necho $seperator \n"
|
|
||||||
"cat /sys/class/thermal/thermal_zone*/type \necho $seperator \n"
|
|
||||||
"cat /sys/class/thermal/thermal_zone*/temp";
|
|
||||||
const shellPath = '.serverbox.sh';
|
|
||||||
|
|
||||||
class ServerProvider extends BusyProvider {
|
class ServerProvider extends BusyProvider {
|
||||||
List<Server> _servers = [];
|
List<Server> _servers = [];
|
||||||
List<Server> get servers => _servers;
|
List<Server> get servers => _servers;
|
||||||
@@ -54,18 +40,6 @@ class ServerProvider extends BusyProvider {
|
|||||||
return Server(spi, initStatus, null, ServerConnectionState.disconnected);
|
return Server(spi, initStatus, null, ServerConnectionState.disconnected);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SSHClient> genClient(ServerPrivateInfo spi) async {
|
|
||||||
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
|
||||||
if (spi.pubKeyId == null) {
|
|
||||||
return SSHClient(socket,
|
|
||||||
username: spi.user, onPasswordRequest: () => spi.pwd);
|
|
||||||
}
|
|
||||||
final key = locator<PrivateKeyStore>().get(spi.pubKeyId!);
|
|
||||||
return SSHClient(socket,
|
|
||||||
username: spi.user,
|
|
||||||
identities: await compute(loadIndentity, key.privateKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> refreshData({ServerPrivateInfo? spi}) async {
|
Future<void> refreshData({ServerPrivateInfo? spi}) async {
|
||||||
if (spi != null) {
|
if (spi != null) {
|
||||||
_getData(spi);
|
_getData(spi);
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
class BuildData {
|
class BuildData {
|
||||||
static const String name = "ServerBox";
|
static const String name = "ServerBox";
|
||||||
static const int build = 201;
|
static const int build = 202;
|
||||||
static const String engine =
|
static const String engine =
|
||||||
"Flutter 3.7.0 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision b06b8b2710 (7 days ago) • 2023-01-23 16:55:55 -0800\nEngine • revision b24591ed32\nTools • Dart 2.19.0 • DevTools 2.20.1\n";
|
"Flutter 3.7.0 • channel stable • https://github.com/flutter/flutter.git\nFramework • revision b06b8b2710 (8 days ago) • 2023-01-23 16:55:55 -0800\nEngine • revision b24591ed32\nTools • Dart 2.19.0 • DevTools 2.20.1\n";
|
||||||
static const String buildAt = "2023-01-31 17:36:05.286138";
|
static const String buildAt = "2023-02-01 10:55:03.386055";
|
||||||
static const int modifications = 5;
|
static const int modifications = 5;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,24 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
|
||||||
import 'package:toolbox/locator.dart';
|
|
||||||
|
|
||||||
Color get primaryColor => Color(locator<SettingStore>().primaryColor.fetch()!);
|
import '../../core/utils/ui.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
import '../store/setting.dart';
|
||||||
|
|
||||||
|
final _primaryColor = locator<SettingStore>().primaryColor.listenable();
|
||||||
|
|
||||||
|
class PrimaryColor extends StatelessWidget {
|
||||||
|
final Widget Function(BuildContext context, Color primaryColor) builder;
|
||||||
|
|
||||||
|
const PrimaryColor({Key? key, required this.builder}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return ValueListenableBuilder<int>(
|
||||||
|
builder: (context, c, child) => builder(context, Color(c)),
|
||||||
|
valueListenable: _primaryColor,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DynamicColor {
|
class DynamicColor {
|
||||||
/// 白天模式显示的颜色
|
/// 白天模式显示的颜色
|
||||||
|
|||||||
12
lib/data/res/server_cmd.dart
Normal file
12
lib/data/res/server_cmd.dart
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
const seperator = 'A====A';
|
||||||
|
const shellCmd = "export LANG=en_US.utf-8 \necho '$seperator' \n"
|
||||||
|
"cat /proc/net/dev && date +%s \necho $seperator \n "
|
||||||
|
"cat /etc/os-release | grep PRETTY_NAME \necho $seperator \n"
|
||||||
|
"cat /proc/stat | grep cpu \necho $seperator \n"
|
||||||
|
"uptime \necho $seperator \n"
|
||||||
|
"cat /proc/net/snmp \necho $seperator \n"
|
||||||
|
"df -h \necho $seperator \n"
|
||||||
|
"cat /proc/meminfo \necho $seperator \n"
|
||||||
|
"cat /sys/class/thermal/thermal_zone*/type \necho $seperator \n"
|
||||||
|
"cat /sys/class/thermal/thermal_zone*/temp";
|
||||||
|
const shellPath = '.serverbox.sh';
|
||||||
@@ -4,17 +4,18 @@ import 'dart:convert';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:toolbox/core/extension/colorx.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../core/extension/colorx.dart';
|
||||||
import 'package:toolbox/data/model/app/backup.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../data/model/app/backup.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../data/res/color.dart';
|
||||||
import 'package:toolbox/data/store/private_key.dart';
|
import '../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/data/store/server.dart';
|
import '../../data/store/private_key.dart';
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
import '../../data/store/server.dart';
|
||||||
import 'package:toolbox/data/store/snippet.dart';
|
import '../../data/store/setting.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../data/store/snippet.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../generated/l10n.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
|
||||||
const backupFormatVersion = 1;
|
const backupFormatVersion = 1;
|
||||||
|
|
||||||
@@ -67,13 +68,14 @@ class BackupPage extends StatelessWidget {
|
|||||||
|
|
||||||
Widget _buildCard(String text, IconData icon, MediaQueryData media,
|
Widget _buildCard(String text, IconData icon, MediaQueryData media,
|
||||||
FutureOr Function() onTap) {
|
FutureOr Function() onTap) {
|
||||||
final priColor = primaryColor;
|
return PrimaryColor(
|
||||||
final textColor = priColor.isBrightColor ? Colors.black : Colors.white;
|
builder: ((context, pColor) {
|
||||||
|
final textColor = pColor.isBrightColor ? Colors.black : Colors.white;
|
||||||
return GestureDetector(
|
return GestureDetector(
|
||||||
onTap: onTap,
|
onTap: onTap,
|
||||||
child: Container(
|
child: Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
borderRadius: BorderRadius.circular(37), color: priColor),
|
borderRadius: BorderRadius.circular(37), color: pColor),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
padding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
||||||
child: Row(
|
child: Row(
|
||||||
@@ -91,6 +93,8 @@ class BackupPage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _showExportDialog(BuildContext context, S s) async {
|
Future<void> _showExportDialog(BuildContext context, S s) async {
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../data/res/color.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import '../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import '../widget/input_field.dart';
|
||||||
|
import '../widget/round_rect_card.dart';
|
||||||
|
|
||||||
class ConvertPage extends StatefulWidget {
|
class ConvertPage extends StatefulWidget {
|
||||||
const ConvertPage({Key? key}) : super(key: key);
|
const ConvertPage({Key? key}) : super(key: key);
|
||||||
@@ -104,6 +105,7 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
'URL $encode',
|
'URL $encode',
|
||||||
'URL $decode'
|
'URL $decode'
|
||||||
];
|
];
|
||||||
|
return PrimaryColor(builder: (context, primaryColor) {
|
||||||
return RoundRectCard(
|
return RoundRectCard(
|
||||||
ExpansionTile(
|
ExpansionTile(
|
||||||
tilePadding: const EdgeInsets.only(left: 7, right: 27),
|
tilePadding: const EdgeInsets.only(left: 7, right: 27),
|
||||||
@@ -116,7 +118,8 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
child: Icon(Icons.change_circle, semanticLabel: _s.upsideDown),
|
child: Icon(Icons.change_circle, semanticLabel: _s.upsideDown),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
final temp = _textEditingController.text;
|
final temp = _textEditingController.text;
|
||||||
_textEditingController.text = _textEditingControllerResult.text;
|
_textEditingController.text =
|
||||||
|
_textEditingControllerResult.text;
|
||||||
_textEditingControllerResult.text = temp;
|
_textEditingControllerResult.text = temp;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -173,6 +176,7 @@ class _ConvertPageState extends State<ConvertPage>
|
|||||||
.toList(),
|
.toList(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildResult() {
|
Widget _buildResult() {
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/model/docker/ps.dart';
|
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
|
||||||
import 'package:toolbox/data/provider/docker.dart';
|
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
|
||||||
import 'package:toolbox/data/res/error.dart';
|
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
|
||||||
import 'package:toolbox/data/res/url.dart';
|
|
||||||
import 'package:toolbox/data/store/docker.dart';
|
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
|
||||||
import 'package:toolbox/locator.dart';
|
|
||||||
import 'package:toolbox/view/widget/center_loading.dart';
|
|
||||||
import 'package:toolbox/view/widget/two_line_text.dart';
|
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
|
||||||
import 'package:toolbox/view/widget/url_text.dart';
|
|
||||||
|
|
||||||
|
import '../../core/utils/ui.dart';
|
||||||
|
import '../../data/model/docker/ps.dart';
|
||||||
|
import '../../data/model/server/server_private_info.dart';
|
||||||
|
import '../../data/provider/docker.dart';
|
||||||
|
import '../../data/provider/server.dart';
|
||||||
|
import '../../data/res/error.dart';
|
||||||
|
import '../../data/res/font_style.dart';
|
||||||
import '../../data/res/menu.dart';
|
import '../../data/res/menu.dart';
|
||||||
|
import '../../data/res/url.dart';
|
||||||
|
import '../../data/store/docker.dart';
|
||||||
|
import '../../generated/l10n.dart';
|
||||||
|
import '../../locator.dart';
|
||||||
|
import '../widget/center_loading.dart';
|
||||||
import '../widget/dropdown_menu.dart';
|
import '../widget/dropdown_menu.dart';
|
||||||
|
import '../widget/round_rect_card.dart';
|
||||||
|
import '../widget/two_line_text.dart';
|
||||||
|
import '../widget/url_text.dart';
|
||||||
|
|
||||||
class DockerManagePage extends StatefulWidget {
|
class DockerManagePage extends StatefulWidget {
|
||||||
final ServerPrivateInfo spi;
|
final ServerPrivateInfo spi;
|
||||||
|
|||||||
@@ -2,31 +2,31 @@ import 'package:after_layout/after_layout.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:toolbox/core/analysis.dart';
|
|
||||||
import 'package:toolbox/core/route.dart';
|
import '../../core/analysis.dart';
|
||||||
import 'package:toolbox/core/update.dart';
|
import '../../core/route.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../core/update.dart';
|
||||||
import 'package:toolbox/data/model/app/navigation_item.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import '../../data/model/app/navigation_item.dart';
|
||||||
import 'package:toolbox/data/res/build_data.dart';
|
import '../../data/provider/server.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../data/res/build_data.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/data/res/icon.dart';
|
import '../../data/res/icon.dart';
|
||||||
import 'package:toolbox/data/res/tab.dart';
|
import '../../data/res/tab.dart';
|
||||||
import 'package:toolbox/data/res/url.dart';
|
import '../../data/res/url.dart';
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
import '../../data/store/setting.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../generated/l10n.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../locator.dart';
|
||||||
import 'package:toolbox/view/page/backup.dart';
|
import '../widget/url_text.dart';
|
||||||
import 'package:toolbox/view/page/convert.dart';
|
import 'backup.dart';
|
||||||
import 'package:toolbox/view/page/debug.dart';
|
import 'convert.dart';
|
||||||
import 'package:toolbox/view/page/ping.dart';
|
import 'debug.dart';
|
||||||
import 'package:toolbox/view/page/private_key/list.dart';
|
import 'ping.dart';
|
||||||
import 'package:toolbox/view/page/server/tab.dart';
|
import 'private_key/list.dart';
|
||||||
import 'package:toolbox/view/page/setting.dart';
|
import 'server/tab.dart';
|
||||||
import 'package:toolbox/view/page/sftp/downloaded.dart';
|
import 'setting.dart';
|
||||||
import 'package:toolbox/view/page/snippet/list.dart';
|
import 'sftp/downloaded.dart';
|
||||||
import 'package:toolbox/view/widget/url_text.dart';
|
import 'snippet/list.dart';
|
||||||
|
|
||||||
class MyHomePage extends StatefulWidget {
|
class MyHomePage extends StatefulWidget {
|
||||||
const MyHomePage({Key? key, required this.primaryColor}) : super(key: key);
|
const MyHomePage({Key? key, required this.primaryColor}) : super(key: key);
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/uint8list.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../core/extension/uint8list.dart';
|
||||||
import 'package:toolbox/data/model/server/ping_result.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import '../../data/model/server/ping_result.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../data/provider/server.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../data/res/color.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/input_field.dart';
|
import '../../locator.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import '../widget/input_field.dart';
|
||||||
|
import '../widget/round_rect_card.dart';
|
||||||
|
|
||||||
final doaminReg =
|
final doaminReg =
|
||||||
RegExp(r'^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$');
|
RegExp(r'^[a-zA-Z0-9][a-zA-Z0-9-]{1,61}[a-zA-Z0-9]\.[a-zA-Z]{2,}$');
|
||||||
@@ -88,9 +89,12 @@ class _PingPageState extends State<PingPage>
|
|||||||
Widget _buildResultItem(PingResult result) {
|
Widget _buildResultItem(PingResult result) {
|
||||||
final unknown = s.unknown;
|
final unknown = s.unknown;
|
||||||
final ms = s.ms;
|
final ms = s.ms;
|
||||||
|
return PrimaryColor(
|
||||||
|
builder: ((context, primaryColor) {
|
||||||
return RoundRectCard(
|
return RoundRectCard(
|
||||||
ListTile(
|
ListTile(
|
||||||
contentPadding: const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
contentPadding:
|
||||||
|
const EdgeInsets.symmetric(vertical: 7, horizontal: 17),
|
||||||
title: Text(
|
title: Text(
|
||||||
result.serverName,
|
result.serverName,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
@@ -112,6 +116,8 @@ class _PingPageState extends State<PingPage>
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
String _buildPingSummary(PingResult result, String unknown, String ms) {
|
String _buildPingSummary(PingResult result, String unknown, String ms) {
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/model/pkg/upgrade_info.dart';
|
import '../../data/model/pkg/upgrade_info.dart';
|
||||||
import 'package:toolbox/data/model/server/dist.dart';
|
import '../../data/model/server/dist.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/pkg.dart';
|
import '../../data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import '../../data/provider/pkg.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../data/provider/server.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/center_loading.dart';
|
import '../../locator.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import '../widget/center_loading.dart';
|
||||||
import 'package:toolbox/view/widget/two_line_text.dart';
|
import '../widget/round_rect_card.dart';
|
||||||
|
import '../widget/two_line_text.dart';
|
||||||
|
|
||||||
class PkgManagePage extends StatefulWidget {
|
class PkgManagePage extends StatefulWidget {
|
||||||
const PkgManagePage(this.spi, {Key? key}) : super(key: key);
|
const PkgManagePage(this.spi, {Key? key}) : super(key: key);
|
||||||
|
|||||||
@@ -3,13 +3,14 @@ import 'package:dartssh2/dartssh2.dart';
|
|||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/private_key.dart';
|
import '../../../data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../../data/provider/private_key.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
import '../../../locator.dart';
|
||||||
|
import '../../widget/input_decoration.dart';
|
||||||
|
|
||||||
const _format = 'text/plain';
|
const _format = 'text/plain';
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,6 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
with SingleTickerProviderStateMixin {
|
with SingleTickerProviderStateMixin {
|
||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
late S _s;
|
late S _s;
|
||||||
late Color pColor;
|
|
||||||
bool _showDistLogo = true;
|
bool _showDistLogo = true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -38,7 +37,6 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
_s = S.of(context);
|
_s = S.of(context);
|
||||||
_showDistLogo = locator<SettingStore>().showDistLogo.fetch()!;
|
_showDistLogo = locator<SettingStore>().showDistLogo.fetch()!;
|
||||||
pColor = primaryColor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -175,12 +173,14 @@ class _ServerDetailPageState extends State<ServerDetailPage>
|
|||||||
Widget _buildProgress(double percent) {
|
Widget _buildProgress(double percent) {
|
||||||
if (percent > 100) percent = 100;
|
if (percent > 100) percent = 100;
|
||||||
final percentWithinOne = percent / 100;
|
final percentWithinOne = percent / 100;
|
||||||
|
return PrimaryColor(builder: (context, primaryColor) {
|
||||||
return LinearProgressIndicator(
|
return LinearProgressIndicator(
|
||||||
value: percentWithinOne,
|
value: percentWithinOne,
|
||||||
minHeight: 7,
|
minHeight: 7,
|
||||||
backgroundColor: progressColor.resolve(context),
|
backgroundColor: progressColor.resolve(context),
|
||||||
color: pColor,
|
color: primaryColor,
|
||||||
);
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUpTimeAndSys(ServerStatus ss) {
|
Widget _buildUpTimeAndSys(ServerStatus ss) {
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/route.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../../core/route.dart';
|
||||||
import 'package:toolbox/data/model/server/private_key_info.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
import '../../../data/model/server/private_key_info.dart';
|
||||||
import 'package:toolbox/data/provider/private_key.dart';
|
import '../../../data/model/server/server_private_info.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import '../../../data/provider/private_key.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../../data/provider/server.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../../data/res/color.dart';
|
||||||
import 'package:toolbox/data/store/private_key.dart';
|
import '../../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../../data/store/private_key.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/page/private_key/edit.dart';
|
import '../../../locator.dart';
|
||||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
import '../../widget/input_decoration.dart';
|
||||||
|
import '../private_key/edit.dart';
|
||||||
|
|
||||||
class ServerEditPage extends StatefulWidget {
|
class ServerEditPage extends StatefulWidget {
|
||||||
const ServerEditPage({Key? key, this.spi}) : super(key: key);
|
const ServerEditPage({Key? key, this.spi}) : super(key: key);
|
||||||
@@ -104,8 +105,11 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
focusNode: _nameFocus,
|
focusNode: _nameFocus,
|
||||||
onSubmitted: (_) => _focusScope.requestFocus(_ipFocus),
|
onSubmitted: (_) => _focusScope.requestFocus(_ipFocus),
|
||||||
decoration: buildDecoration(_s.name,
|
decoration: buildDecoration(
|
||||||
icon: Icons.info, hint: _s.exampleName),
|
_s.name,
|
||||||
|
icon: Icons.info,
|
||||||
|
hint: _s.exampleName,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _ipController,
|
controller: _ipController,
|
||||||
@@ -114,16 +118,22 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
focusNode: _ipFocus,
|
focusNode: _ipFocus,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
decoration: buildDecoration(_s.host,
|
decoration: buildDecoration(
|
||||||
icon: Icons.storage, hint: 'example.com'),
|
_s.host,
|
||||||
|
icon: Icons.storage,
|
||||||
|
hint: 'example.com',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _portController,
|
controller: _portController,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
focusNode: _portFocus,
|
focusNode: _portFocus,
|
||||||
onSubmitted: (_) => _focusScope.requestFocus(_usernameFocus),
|
onSubmitted: (_) => _focusScope.requestFocus(_usernameFocus),
|
||||||
decoration: buildDecoration(_s.port,
|
decoration: buildDecoration(
|
||||||
icon: Icons.format_list_numbered, hint: '22'),
|
_s.port,
|
||||||
|
icon: Icons.format_list_numbered,
|
||||||
|
hint: '22',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
TextField(
|
TextField(
|
||||||
controller: _usernameController,
|
controller: _usernameController,
|
||||||
@@ -131,8 +141,11 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
focusNode: _usernameFocus,
|
focusNode: _usernameFocus,
|
||||||
autocorrect: false,
|
autocorrect: false,
|
||||||
enableSuggestions: false,
|
enableSuggestions: false,
|
||||||
decoration: buildDecoration(_s.user,
|
decoration: buildDecoration(
|
||||||
icon: Icons.account_box, hint: 'root'),
|
_s.user,
|
||||||
|
icon: Icons.account_box,
|
||||||
|
hint: 'root',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 7),
|
const SizedBox(height: 7),
|
||||||
Row(
|
Row(
|
||||||
@@ -140,7 +153,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
Text(_s.keyAuth),
|
Text(_s.keyAuth),
|
||||||
Switch(
|
Switch(
|
||||||
value: usePublicKey,
|
value: usePublicKey,
|
||||||
onChanged: (val) => setState(() => usePublicKey = val)),
|
onChanged: (val) => setState(() => usePublicKey = val),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
!usePublicKey
|
!usePublicKey
|
||||||
@@ -148,8 +162,11 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
controller: _passwordController,
|
controller: _passwordController,
|
||||||
obscureText: true,
|
obscureText: true,
|
||||||
keyboardType: TextInputType.text,
|
keyboardType: TextInputType.text,
|
||||||
decoration: buildDecoration(_s.pwd,
|
decoration: buildDecoration(
|
||||||
icon: Icons.password, hint: _s.pwd),
|
_s.pwd,
|
||||||
|
icon: Icons.password,
|
||||||
|
hint: _s.pwd,
|
||||||
|
),
|
||||||
onSubmitted: (_) => {},
|
onSubmitted: (_) => {},
|
||||||
)
|
)
|
||||||
: const SizedBox(),
|
: const SizedBox(),
|
||||||
@@ -166,7 +183,8 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
(e) => ListTile(
|
(e) => ListTile(
|
||||||
contentPadding: EdgeInsets.zero,
|
contentPadding: EdgeInsets.zero,
|
||||||
title: Text(e.id, textAlign: TextAlign.start),
|
title: Text(e.id, textAlign: TextAlign.start),
|
||||||
trailing: _buildRadio(key.infos.indexOf(e), e)),
|
trailing: _buildRadio(key.infos.indexOf(e), e),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
.toList();
|
.toList();
|
||||||
tiles.add(
|
tiles.add(
|
||||||
@@ -177,11 +195,12 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
onPressed: () => AppRoute(
|
onPressed: () => AppRoute(
|
||||||
const PrivateKeyEditPage(),
|
const PrivateKeyEditPage(),
|
||||||
'private key edit page')
|
'private key edit page',
|
||||||
.go(context),
|
).go(context),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
return PrimaryColor(builder: ((context, primaryColor) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: primaryColor,
|
textColor: primaryColor,
|
||||||
iconColor: primaryColor,
|
iconColor: primaryColor,
|
||||||
@@ -193,6 +212,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
),
|
),
|
||||||
children: tiles,
|
children: tiles,
|
||||||
);
|
);
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: const SizedBox()
|
: const SizedBox()
|
||||||
|
|||||||
@@ -3,32 +3,32 @@ import 'package:circle_chart/circle_chart.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/route.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/model/server/server.dart';
|
|
||||||
import 'package:toolbox/data/model/server/server_private_info.dart';
|
|
||||||
import 'package:toolbox/data/model/server/server_status.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/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/pkg.dart';
|
|
||||||
import 'package:toolbox/view/page/docker.dart';
|
|
||||||
import 'package:toolbox/view/page/server/detail.dart';
|
|
||||||
import 'package:toolbox/view/page/server/edit.dart';
|
|
||||||
import 'package:toolbox/view/page/sftp/view.dart';
|
|
||||||
import 'package:toolbox/view/page/snippet/edit.dart';
|
|
||||||
import 'package:toolbox/view/page/ssh.dart';
|
|
||||||
import 'package:toolbox/view/widget/picker.dart';
|
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
|
||||||
|
|
||||||
|
import '../../../core/route.dart';
|
||||||
|
import '../../../core/utils/ui.dart';
|
||||||
|
import '../../../data/model/server/server.dart';
|
||||||
|
import '../../../data/model/server/server_private_info.dart';
|
||||||
|
import '../../../data/model/server/server_status.dart';
|
||||||
|
import '../../../data/provider/server.dart';
|
||||||
|
import '../../../data/provider/snippet.dart';
|
||||||
|
import '../../../data/res/color.dart';
|
||||||
|
import '../../../data/res/font_style.dart';
|
||||||
import '../../../data/res/menu.dart';
|
import '../../../data/res/menu.dart';
|
||||||
|
import '../../../data/res/url.dart';
|
||||||
|
import '../../../data/store/setting.dart';
|
||||||
|
import '../../../generated/l10n.dart';
|
||||||
|
import '../../../locator.dart';
|
||||||
import '../../widget/dropdown_menu.dart';
|
import '../../widget/dropdown_menu.dart';
|
||||||
|
import '../../widget/picker.dart';
|
||||||
|
import '../../widget/round_rect_card.dart';
|
||||||
import '../../widget/url_text.dart';
|
import '../../widget/url_text.dart';
|
||||||
|
import '../docker.dart';
|
||||||
|
import '../pkg.dart';
|
||||||
|
import '../sftp/view.dart';
|
||||||
|
import '../snippet/edit.dart';
|
||||||
|
import '../ssh.dart';
|
||||||
|
import 'detail.dart';
|
||||||
|
import 'edit.dart';
|
||||||
|
|
||||||
class ServerPage extends StatefulWidget {
|
class ServerPage extends StatefulWidget {
|
||||||
const ServerPage({Key? key}) : super(key: key);
|
const ServerPage({Key? key}) : super(key: key);
|
||||||
@@ -41,7 +41,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
with AutomaticKeepAliveClientMixin, AfterLayoutMixin {
|
with AutomaticKeepAliveClientMixin, AfterLayoutMixin {
|
||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
late ThemeData _theme;
|
late ThemeData _theme;
|
||||||
late Color _primaryColor;
|
|
||||||
late ServerProvider _serverProvider;
|
late ServerProvider _serverProvider;
|
||||||
late SettingStore _settingStore;
|
late SettingStore _settingStore;
|
||||||
late S _s;
|
late S _s;
|
||||||
@@ -58,7 +57,6 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
_theme = Theme.of(context);
|
_theme = Theme.of(context);
|
||||||
_primaryColor = primaryColor;
|
|
||||||
_s = S.of(context);
|
_s = S.of(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,9 +92,10 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: child,
|
body: child,
|
||||||
floatingActionButton: FloatingActionButton(
|
floatingActionButton: FloatingActionButton(
|
||||||
onPressed: () =>
|
onPressed: () => AppRoute(
|
||||||
AppRoute(const ServerEditPage(), 'Add server info page')
|
const ServerEditPage(),
|
||||||
.go(context),
|
'Add server info page',
|
||||||
|
).go(context),
|
||||||
tooltip: _s.addAServer,
|
tooltip: _s.addAServer,
|
||||||
heroTag: 'server page fab',
|
heroTag: 'server page fab',
|
||||||
child: const Icon(Icons.add),
|
child: const Icon(Icons.add),
|
||||||
@@ -353,12 +352,16 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
child: Stack(
|
child: Stack(
|
||||||
children: [
|
children: [
|
||||||
Center(
|
Center(
|
||||||
child: CircleChart(
|
child: PrimaryColor(
|
||||||
progressColor: _primaryColor,
|
builder: (context, primaryColor) {
|
||||||
|
return CircleChart(
|
||||||
|
progressColor: primaryColor,
|
||||||
progressNumber: percent,
|
progressNumber: percent,
|
||||||
maxNumber: 100,
|
maxNumber: 100,
|
||||||
width: 53,
|
width: 53,
|
||||||
height: 53,
|
height: 53,
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Positioned.fill(
|
Positioned.fill(
|
||||||
@@ -404,8 +407,10 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
showRoundDialog(
|
showRoundDialog(
|
||||||
context,
|
context,
|
||||||
_s.choose,
|
_s.choose,
|
||||||
buildPicker(provider.snippets.map((e) => Text(e.name)).toList(),
|
buildPicker(
|
||||||
(idx) => snippet = provider.snippets[idx]),
|
provider.snippets.map((e) => Text(e.name)).toList(),
|
||||||
|
(idx) => snippet = provider.snippets[idx],
|
||||||
|
),
|
||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -419,7 +424,8 @@ class _ServerPageState extends State<ServerPage>
|
|||||||
[
|
[
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () => Navigator.of(context).pop(),
|
onPressed: () => Navigator.of(context).pop(),
|
||||||
child: Text(_s.ok))
|
child: Text(_s.ok),
|
||||||
|
)
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,18 +1,19 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
|
import 'package:flutter_material_color_picker/flutter_material_color_picker.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/update.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../core/update.dart';
|
||||||
import 'package:toolbox/data/provider/app.dart';
|
import '../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/server.dart';
|
import '../../data/provider/app.dart';
|
||||||
import 'package:toolbox/data/res/build_data.dart';
|
import '../../data/provider/server.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
import '../../data/res/build_data.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../data/res/color.dart';
|
||||||
import 'package:toolbox/data/res/tab.dart';
|
import '../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/data/store/setting.dart';
|
import '../../data/res/tab.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../data/store/setting.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import '../../locator.dart';
|
||||||
|
import '../widget/round_rect_card.dart';
|
||||||
|
|
||||||
class SettingPage extends StatefulWidget {
|
class SettingPage extends StatefulWidget {
|
||||||
const SettingPage({Key? key}) : super(key: key);
|
const SettingPage({Key? key}) : super(key: key);
|
||||||
@@ -25,7 +26,6 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
late final SettingStore _setting;
|
late final SettingStore _setting;
|
||||||
late int _selectedColorValue;
|
late int _selectedColorValue;
|
||||||
late int _launchPageIdx;
|
late int _launchPageIdx;
|
||||||
late Color priColor;
|
|
||||||
late final ServerProvider _serverProvider;
|
late final ServerProvider _serverProvider;
|
||||||
late MediaQueryData _media;
|
late MediaQueryData _media;
|
||||||
late ThemeData _theme;
|
late ThemeData _theme;
|
||||||
@@ -36,7 +36,6 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
@override
|
@override
|
||||||
void didChangeDependencies() {
|
void didChangeDependencies() {
|
||||||
super.didChangeDependencies();
|
super.didChangeDependencies();
|
||||||
priColor = primaryColor;
|
|
||||||
_media = MediaQuery.of(context);
|
_media = MediaQuery.of(context);
|
||||||
_theme = Theme.of(context);
|
_theme = Theme.of(context);
|
||||||
_s = S.of(context);
|
_s = S.of(context);
|
||||||
@@ -57,15 +56,19 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(_s.setting),
|
title: Text(_s.setting),
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: PrimaryColor(
|
||||||
|
builder: (context, primaryColor) {
|
||||||
|
return ListView(
|
||||||
padding: const EdgeInsets.all(17),
|
padding: const EdgeInsets.all(17),
|
||||||
children: [
|
children: [
|
||||||
_buildAppColorPreview(),
|
_buildAppColorPreview(primaryColor),
|
||||||
_buildUpdateInterval(),
|
_buildUpdateInterval(primaryColor),
|
||||||
_buildCheckUpdate(),
|
_buildCheckUpdate(),
|
||||||
_buildLaunchPage(),
|
_buildLaunchPage(primaryColor),
|
||||||
_buildDistLogoSwitch(),
|
_buildDistLogoSwitch(),
|
||||||
].map((e) => RoundRectCard(e)).toList(),
|
].map((e) => RoundRectCard(e)).toList(),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -110,7 +113,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUpdateInterval() {
|
Widget _buildUpdateInterval(Color priColor) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: priColor,
|
textColor: priColor,
|
||||||
title: Text(
|
title: Text(
|
||||||
@@ -159,7 +162,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildAppColorPreview() {
|
Widget _buildAppColorPreview(Color priColor) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: priColor,
|
textColor: priColor,
|
||||||
trailing: ClipOval(
|
trailing: ClipOval(
|
||||||
@@ -196,7 +199,7 @@ class _SettingPageState extends State<SettingPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildLaunchPage() {
|
Widget _buildLaunchPage(Color priColor) {
|
||||||
return ExpansionTile(
|
return ExpansionTile(
|
||||||
textColor: priColor,
|
textColor: priColor,
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/extension/numx.dart';
|
|
||||||
import 'package:toolbox/core/extension/stringx.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import 'package:toolbox/core/route.dart';
|
import '../../../core/extension/stringx.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../../core/route.dart';
|
||||||
import 'package:toolbox/data/model/app/path_with_prefix.dart';
|
import '../../../core/utils/misc.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/res/path.dart';
|
import '../../../data/model/app/path_with_prefix.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/view/page/sftp/downloading.dart';
|
import '../../../data/res/path.dart';
|
||||||
import 'package:toolbox/view/widget/fade_in.dart';
|
import '../../../generated/l10n.dart';
|
||||||
|
import '../../widget/fade_in.dart';
|
||||||
|
import 'downloading.dart';
|
||||||
|
|
||||||
class SFTPDownloadedPage extends StatefulWidget {
|
class SFTPDownloadedPage extends StatefulWidget {
|
||||||
const SFTPDownloadedPage({Key? key}) : super(key: key);
|
const SFTPDownloadedPage({Key? key}) : super(key: key);
|
||||||
|
|||||||
@@ -1,13 +1,15 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/core/extension/numx.dart';
|
|
||||||
import 'package:toolbox/core/utils.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import 'package:toolbox/data/model/sftp/download_status.dart';
|
import '../../../core/utils/misc.dart';
|
||||||
import 'package:toolbox/data/provider/sftp_download.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../../data/model/sftp/download_status.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../../data/provider/sftp_download.dart';
|
||||||
import 'package:toolbox/view/widget/center_loading.dart';
|
import '../../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/view/widget/round_rect_card.dart';
|
import '../../../generated/l10n.dart';
|
||||||
|
import '../../widget/center_loading.dart';
|
||||||
|
import '../../widget/round_rect_card.dart';
|
||||||
|
|
||||||
class SFTPDownloadingPage extends StatefulWidget {
|
class SFTPDownloadingPage extends StatefulWidget {
|
||||||
const SFTPDownloadingPage({Key? key}) : super(key: key);
|
const SFTPDownloadingPage({Key? key}) : super(key: key);
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../../../core/extension/numx.dart';
|
import '../../../core/extension/numx.dart';
|
||||||
import '../../../core/extension/stringx.dart';
|
import '../../../core/extension/stringx.dart';
|
||||||
import '../../../core/route.dart';
|
import '../../../core/route.dart';
|
||||||
import '../../../core/utils.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import '../../../data/model/server/server.dart';
|
import '../../../data/model/server/server.dart';
|
||||||
import '../../../data/model/server/server_private_info.dart';
|
import '../../../data/model/server/server_private_info.dart';
|
||||||
import '../../../data/model/sftp/absolute_path.dart';
|
import '../../../data/model/sftp/absolute_path.dart';
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
import 'package:after_layout/after_layout.dart';
|
import 'package:after_layout/after_layout.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
import 'package:toolbox/data/model/server/snippet.dart';
|
import '../../../core/utils/ui.dart';
|
||||||
import 'package:toolbox/data/provider/snippet.dart';
|
import '../../../data/model/server/snippet.dart';
|
||||||
import 'package:toolbox/data/res/font_style.dart';
|
import '../../../data/provider/snippet.dart';
|
||||||
import 'package:toolbox/generated/l10n.dart';
|
import '../../../data/res/font_style.dart';
|
||||||
import 'package:toolbox/locator.dart';
|
import '../../../generated/l10n.dart';
|
||||||
import 'package:toolbox/view/widget/input_decoration.dart';
|
import '../../../locator.dart';
|
||||||
|
import '../../widget/input_decoration.dart';
|
||||||
|
|
||||||
class SnippetEditPage extends StatefulWidget {
|
class SnippetEditPage extends StatefulWidget {
|
||||||
const SnippetEditPage({Key? key, this.snippet}) : super(key: key);
|
const SnippetEditPage({Key? key, this.snippet}) : super(key: key);
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:toolbox/data/res/color.dart';
|
|
||||||
import 'package:xterm/xterm.dart';
|
import 'package:xterm/xterm.dart';
|
||||||
|
|
||||||
import '../../core/utils.dart';
|
import '../../core/utils/ui.dart';
|
||||||
|
import '../../core/utils/server.dart';
|
||||||
import '../../data/model/server/server_private_info.dart';
|
import '../../data/model/server/server_private_info.dart';
|
||||||
import '../../data/model/ssh/virtual_key.dart';
|
import '../../data/model/ssh/virtual_key.dart';
|
||||||
import '../../data/provider/virtual_keyboard.dart';
|
import '../../data/provider/virtual_keyboard.dart';
|
||||||
|
import '../../data/res/color.dart';
|
||||||
import '../../data/res/terminal_theme.dart';
|
import '../../data/res/terminal_theme.dart';
|
||||||
import '../../data/res/virtual_key.dart';
|
import '../../data/res/virtual_key.dart';
|
||||||
import '../../data/store/private_key.dart';
|
|
||||||
import '../../locator.dart';
|
import '../../locator.dart';
|
||||||
|
|
||||||
class SSHPage extends StatefulWidget {
|
class SSHPage extends StatefulWidget {
|
||||||
@@ -156,13 +156,15 @@ class _SSHPageState extends State<SSHPage> {
|
|||||||
color: isDark ? Colors.white : Colors.black,
|
color: isDark ? Colors.white : Colors.black,
|
||||||
size: 17,
|
size: 17,
|
||||||
)
|
)
|
||||||
: Text(
|
: PrimaryColor(builder: (context, color) {
|
||||||
|
return Text(
|
||||||
item.text,
|
item.text,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: selected ? primaryColor : null,
|
color: selected ? color : Colors.black,
|
||||||
fontSize: 15,
|
fontSize: 17,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
});
|
||||||
|
|
||||||
return InkWell(
|
return InkWell(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
@@ -201,20 +203,3 @@ class _SSHPageState extends State<SSHPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<SSHClient> genClient(ServerPrivateInfo spi) async {
|
|
||||||
final socket = await SSHSocket.connect(spi.ip, spi.port);
|
|
||||||
if (spi.pubKeyId == null) {
|
|
||||||
return SSHClient(
|
|
||||||
socket,
|
|
||||||
username: spi.user,
|
|
||||||
onPasswordRequest: () => spi.pwd,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final key = locator<PrivateKeyStore>().get(spi.pubKeyId!);
|
|
||||||
return SSHClient(
|
|
||||||
socket,
|
|
||||||
username: spi.user,
|
|
||||||
identities: await compute(loadIndentity, key.privateKey),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -14,7 +14,12 @@ class DropdownBtnItem {
|
|||||||
|
|
||||||
Widget build(S s) => Row(
|
Widget build(S s) => Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(icon, color: primaryColor),
|
PrimaryColor(builder: (context, primaryColor) {
|
||||||
|
return Icon(
|
||||||
|
icon,
|
||||||
|
color: primaryColor,
|
||||||
|
);
|
||||||
|
}),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 10,
|
width: 10,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ InputDecoration buildDecoration(String label,
|
|||||||
labelText: label,
|
labelText: label,
|
||||||
labelStyle: textStyle,
|
labelStyle: textStyle,
|
||||||
hintText: hint,
|
hintText: hint,
|
||||||
icon: Icon(
|
icon: PrimaryColor(builder: (context, primaryColor) {
|
||||||
|
return Icon(
|
||||||
icon,
|
icon,
|
||||||
color: primaryColor,
|
color: primaryColor,
|
||||||
));
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:flutter/gestures.dart';
|
import 'package:flutter/gestures.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:toolbox/core/utils.dart';
|
|
||||||
|
import '../../core/utils/ui.dart';
|
||||||
|
|
||||||
const regUrl =
|
const regUrl =
|
||||||
r"(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]*";
|
r"(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]*";
|
||||||
|
|||||||
@@ -730,10 +730,10 @@ packages:
|
|||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
name: r_upgrade
|
name: r_upgrade
|
||||||
sha256: "5a597989ca065a47d62a992b23de068a118f1616a49bfa2518d552466c4ddc7b"
|
sha256: be460ed1d2bf3b444a731aa2eeb38751faaef91097fed4bf9d138d3214b98999
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.4.1"
|
version: "0.3.8+2"
|
||||||
share_plus:
|
share_plus:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ dependencies:
|
|||||||
url: https://github.com/lollipopkit/circle_chart
|
url: https://github.com/lollipopkit/circle_chart
|
||||||
ref: main
|
ref: main
|
||||||
# path: ../circle_chart
|
# path: ../circle_chart
|
||||||
r_upgrade: ^0.4.1
|
r_upgrade: ^0.3.6
|
||||||
path_provider: ^2.0.9
|
path_provider: ^2.0.9
|
||||||
easy_isolate: ^1.3.0
|
easy_isolate: ^1.3.0
|
||||||
share_plus: ^6.3.0
|
share_plus: ^6.3.0
|
||||||
|
|||||||
Reference in New Issue
Block a user