mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 19:22:22 +01:00
<feat: New release>
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart' as fluent show showDialog;
|
||||
import 'package:reboot_launcher/src/dialog/message.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||
|
||||
import 'dialog_button.dart';
|
||||
|
||||
Future<T?> showDialog<T extends Object?>({required WidgetBuilder builder}) => fluent.showDialog(
|
||||
context: pageKey.currentContext!,
|
||||
Future<T?> showAppDialog<T extends Object?>({required WidgetBuilder builder}) => fluent.showDialog(
|
||||
context: appKey.currentContext!,
|
||||
useRootNavigator: false,
|
||||
builder: builder
|
||||
);
|
||||
@@ -24,7 +24,7 @@ class GenericDialog extends AbstractDialog {
|
||||
final List<DialogButton> buttons;
|
||||
final EdgeInsets? padding;
|
||||
|
||||
const GenericDialog({super.key, required this.header, required this.buttons, this.padding});
|
||||
const GenericDialog({Key? key, required this.header, required this.buttons, this.padding}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => ContentDialog(
|
||||
@@ -40,7 +40,7 @@ class FormDialog extends AbstractDialog {
|
||||
final Widget content;
|
||||
final List<DialogButton> buttons;
|
||||
|
||||
const FormDialog({super.key, required this.content, required this.buttons});
|
||||
const FormDialog({Key? key, required this.content, required this.buttons}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -80,10 +80,10 @@ class InfoDialog extends AbstractDialog {
|
||||
final String text;
|
||||
final List<DialogButton>? buttons;
|
||||
|
||||
const InfoDialog({required this.text, this.buttons, super.key});
|
||||
const InfoDialog({required this.text, this.buttons, Key? key}) : super(key: key);
|
||||
|
||||
InfoDialog.ofOnly({required this.text, required DialogButton button, super.key})
|
||||
: buttons = [button];
|
||||
InfoDialog.ofOnly({required this.text, required DialogButton button, Key? key})
|
||||
: buttons = [button], super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -109,7 +109,7 @@ class ProgressDialog extends AbstractDialog {
|
||||
final String text;
|
||||
final Function()? onStop;
|
||||
|
||||
const ProgressDialog({required this.text, this.onStop, super.key});
|
||||
const ProgressDialog({required this.text, this.onStop, Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -143,14 +143,14 @@ class FutureBuilderDialog extends AbstractDialog {
|
||||
final bool closeAutomatically;
|
||||
|
||||
const FutureBuilderDialog(
|
||||
{super.key,
|
||||
{Key? key,
|
||||
required this.future,
|
||||
required this.loadingMessage,
|
||||
required this.successfulBody,
|
||||
required this.unsuccessfulBody,
|
||||
required this.errorMessageBuilder,
|
||||
this.onError,
|
||||
this.closeAutomatically = false});
|
||||
this.closeAutomatically = false}) : super(key: key);
|
||||
|
||||
static Container ofMessage(String message) {
|
||||
return Container(
|
||||
@@ -223,14 +223,14 @@ class ErrorDialog extends AbstractDialog {
|
||||
final StackTrace? stackTrace;
|
||||
final Function(Object) errorMessageBuilder;
|
||||
|
||||
const ErrorDialog({super.key, required this.exception, required this.errorMessageBuilder, this.stackTrace});
|
||||
const ErrorDialog({Key? key, required this.exception, required this.errorMessageBuilder, this.stackTrace}) : super(key: key);
|
||||
|
||||
static DialogButton createCopyErrorButton({required Object error, required StackTrace? stackTrace, required Function() onClick, ButtonType type = ButtonType.primary}) => DialogButton(
|
||||
text: "Copy error",
|
||||
type: type,
|
||||
onTap: () async {
|
||||
FlutterClipboard.controlC("An error occurred: $error\nStacktrace:\n $stackTrace");
|
||||
showMessage("Copied error to clipboard");
|
||||
showInfoBar("Copied error to clipboard");
|
||||
onClick();
|
||||
},
|
||||
);
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
class DialogButton extends StatefulWidget {
|
||||
final String? text;
|
||||
80
gui/lib/src/dialog/abstract/info_bar.dart
Normal file
80
gui/lib/src/dialog/abstract/info_bar.dart
Normal file
@@ -0,0 +1,80 @@
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||
import 'package:sync/semaphore.dart';
|
||||
|
||||
Semaphore _semaphore = Semaphore();
|
||||
HashMap<int, OverlayEntry?> _overlays = HashMap();
|
||||
|
||||
void restoreMessage(int lastIndex) {
|
||||
removeMessage(lastIndex);
|
||||
var overlay = _overlays[pageIndex.value];
|
||||
if(overlay == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Overlay.of(pageKey.currentContext!).insert(overlay);
|
||||
}
|
||||
|
||||
void showInfoBar(String text, {InfoBarSeverity severity = InfoBarSeverity.info, bool loading = false, Duration? duration = snackbarShortDuration, Widget? action}) {
|
||||
try {
|
||||
_semaphore.acquire();
|
||||
var index = pageIndex.value;
|
||||
removeMessage(index);
|
||||
var overlay = showSnackbar(
|
||||
pageKey.currentContext!,
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: Mica(
|
||||
child: InfoBar(
|
||||
title: Text(text),
|
||||
isLong: action == null,
|
||||
isIconVisible: true,
|
||||
content: action ?? SizedBox(
|
||||
width: double.infinity,
|
||||
child: loading ? const ProgressBar() : const SizedBox()
|
||||
),
|
||||
severity: severity
|
||||
),
|
||||
),
|
||||
),
|
||||
margin: EdgeInsets.only(
|
||||
right: 12.0,
|
||||
left: 12.0,
|
||||
bottom: index == 0 || index == 1 || index == 3 || index == 4 ? 72.0 : 16.0
|
||||
),
|
||||
duration: duration
|
||||
);
|
||||
_overlays[index] = overlay;
|
||||
if(duration != null) {
|
||||
Future.delayed(duration).then((_) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) async {
|
||||
if(_overlays[index] == overlay) {
|
||||
if(overlay.mounted) {
|
||||
overlay.remove();
|
||||
}
|
||||
|
||||
_overlays[index] = null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}finally {
|
||||
_semaphore.release();
|
||||
}
|
||||
}
|
||||
|
||||
void removeMessage(int index) {
|
||||
try {
|
||||
var lastOverlay = _overlays[index];
|
||||
if(lastOverlay != null) {
|
||||
lastOverlay.remove();
|
||||
_overlays[index] = null;
|
||||
}
|
||||
}catch(_) {
|
||||
// Do not use .isMounted
|
||||
// This is intended behaviour
|
||||
}
|
||||
}
|
||||
36
gui/lib/src/dialog/implementation/error.dart
Normal file
36
gui/lib/src/dialog/implementation/error.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
|
||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/dialog.dart';
|
||||
|
||||
|
||||
String? lastError;
|
||||
|
||||
void onError(Object? exception, StackTrace? stackTrace, bool framework) {
|
||||
if(exception == null){
|
||||
return;
|
||||
}
|
||||
|
||||
if(pageKey.currentContext == null || pageKey.currentState?.mounted == false){
|
||||
return;
|
||||
}
|
||||
|
||||
if(lastError == exception.toString()){
|
||||
return;
|
||||
}
|
||||
|
||||
lastError = exception.toString();
|
||||
var route = ModalRoute.of(pageKey.currentContext!);
|
||||
if(route != null && !route.isCurrent){
|
||||
Navigator.of(pageKey.currentContext!).pop(false);
|
||||
}
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((timeStamp) => showAppDialog(
|
||||
builder: (context) =>
|
||||
ErrorDialog(
|
||||
exception: exception,
|
||||
stackTrace: stackTrace,
|
||||
errorMessageBuilder: (exception) => framework ? "An error was thrown by Flutter: $exception" : "An uncaught error was thrown: $exception"
|
||||
)
|
||||
));
|
||||
}
|
||||
69
gui/lib/src/dialog/implementation/game.dart
Normal file
69
gui/lib/src/dialog/implementation/game.dart
Normal file
@@ -0,0 +1,69 @@
|
||||
import 'package:reboot_common/common.dart';
|
||||
|
||||
import '../abstract/dialog.dart';
|
||||
|
||||
const String _unsupportedServerError = "The build you are currently using is not supported by Reboot. "
|
||||
"If you are unsure which version works best, use build 7.40. "
|
||||
"If you are a passionate programmer you can add support by opening a PR on Github. ";
|
||||
|
||||
const String _corruptedBuildError = "An unknown occurred while launching Fortnite. "
|
||||
"Some critical files could be missing in your installation. "
|
||||
"Download the build again from the launcher, not locally, or from a different source. "
|
||||
"Alternatively, something could have gone wrong in the launcher. ";
|
||||
|
||||
Future<void> showMissingDllError(String name) async {
|
||||
showAppDialog(
|
||||
builder: (context) => InfoDialog(
|
||||
text: "$name dll is not a valid dll, fix it in the settings tab"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showTokenErrorFixable() async {
|
||||
showAppDialog(
|
||||
builder: (context) => const InfoDialog(
|
||||
text: "A token error occurred. "
|
||||
"The backend server has been automatically restarted to fix the issue. "
|
||||
"The game has been restarted automatically. "
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showTokenErrorUnfixable() async {
|
||||
showAppDialog(
|
||||
builder: (context) => const InfoDialog(
|
||||
text: "A token error occurred. "
|
||||
"This issue cannot be resolved automatically as the server isn't embedded."
|
||||
"Please restart the server manually, then relaunch your game to check if the issue has been fixed. "
|
||||
"Otherwise, open an issue on Discord."
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showCorruptedBuildError(bool server, [Object? error, StackTrace? stackTrace]) async {
|
||||
if(error == null) {
|
||||
showAppDialog(
|
||||
builder: (context) => InfoDialog(
|
||||
text: server ? _unsupportedServerError : _corruptedBuildError
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
showAppDialog(
|
||||
builder: (context) => ErrorDialog(
|
||||
exception: error,
|
||||
stackTrace: stackTrace,
|
||||
errorMessageBuilder: (exception) => "${_corruptedBuildError}Error message: $exception"
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> showMissingBuildError(FortniteVersion version) async {
|
||||
showAppDialog(
|
||||
builder: (context) => InfoDialog(
|
||||
text: "${version.location.path} no longer contains a Fortnite executable. "
|
||||
"This probably means that you deleted it or move it somewhere else."
|
||||
)
|
||||
);
|
||||
}
|
||||
83
gui/lib/src/dialog/implementation/profile.dart
Normal file
83
gui/lib/src/dialog/implementation/profile.dart
Normal file
@@ -0,0 +1,83 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/material.dart' show Icons;
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/dialog.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/dialog_button.dart';
|
||||
|
||||
final GameController _gameController = Get.find<GameController>();
|
||||
|
||||
Future<bool> showProfileForm(BuildContext context) async{
|
||||
var showPassword = RxBool(false);
|
||||
var oldUsername = _gameController.username.text;
|
||||
var showPasswordTrailing = RxBool(oldUsername.isNotEmpty);
|
||||
var oldPassword = _gameController.password.text;
|
||||
var result = await showAppDialog<bool?>(
|
||||
builder: (context) => Obx(() => FormDialog(
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InfoLabel(
|
||||
label: "Username/Email",
|
||||
child: TextFormBox(
|
||||
placeholder: "Type your username or email",
|
||||
controller: _gameController.username,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
enableSuggestions: true,
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
)
|
||||
),
|
||||
const SizedBox(height: 16.0),
|
||||
InfoLabel(
|
||||
label: "Password",
|
||||
child: TextFormBox(
|
||||
placeholder: "Type your password, if you have one",
|
||||
controller: _gameController.password,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
obscureText: !showPassword.value,
|
||||
enableSuggestions: false,
|
||||
autocorrect: false,
|
||||
onChanged: (text) => showPasswordTrailing.value = text.isNotEmpty,
|
||||
suffix: Button(
|
||||
onPressed: () => showPassword.value = !showPassword.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
showPassword.value ? Icons.visibility_off : Icons.visibility,
|
||||
color: showPasswordTrailing.value ? null : Colors.transparent
|
||||
),
|
||||
)
|
||||
)
|
||||
),
|
||||
const SizedBox(height: 8.0)
|
||||
],
|
||||
),
|
||||
buttons: [
|
||||
DialogButton(
|
||||
text: "Cancel",
|
||||
type: ButtonType.secondary
|
||||
),
|
||||
|
||||
DialogButton(
|
||||
text: "Save",
|
||||
type: ButtonType.primary,
|
||||
onTap: () {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
)
|
||||
]
|
||||
))
|
||||
) ?? false;
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
_gameController.username.text = oldUsername;
|
||||
_gameController.password.text = oldPassword;
|
||||
return false;
|
||||
}
|
||||
288
gui/lib/src/dialog/implementation/server.dart
Normal file
288
gui/lib/src/dialog/implementation/server.dart
Normal file
@@ -0,0 +1,288 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:clipboard/clipboard.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter/material.dart' show Icons;
|
||||
import 'package:get/get_rx/src/rx_types/rx_types.dart';
|
||||
import 'package:get/get_state_manager/src/rx_flutter/rx_obx_widget.dart';
|
||||
import 'package:reboot_launcher/src/controller/matchmaker_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/server_controller.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/dialog.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/dialog_button.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart';
|
||||
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||
import 'package:reboot_launcher/src/util/cryptography.dart';
|
||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||
|
||||
extension ServerControllerDialog on ServerController {
|
||||
Future<bool> restartInteractive() async {
|
||||
var stream = restart();
|
||||
return await _handleStream(stream, false);
|
||||
}
|
||||
|
||||
Future<bool> toggleInteractive([bool showSuccessMessage = true]) async {
|
||||
var stream = toggle();
|
||||
return await _handleStream(stream, showSuccessMessage);
|
||||
}
|
||||
|
||||
Future<bool> _handleStream(Stream<ServerResult> stream, bool showSuccessMessage) async {
|
||||
var completer = Completer<bool>();
|
||||
stream.listen((event) {
|
||||
switch (event.type) {
|
||||
case ServerResultType.starting:
|
||||
showInfoBar(
|
||||
"Starting the $controllerName...",
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
break;
|
||||
case ServerResultType.startSuccess:
|
||||
if(showSuccessMessage) {
|
||||
showInfoBar(
|
||||
"The $controllerName was started successfully",
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
}
|
||||
completer.complete(true);
|
||||
break;
|
||||
case ServerResultType.startError:
|
||||
showInfoBar(
|
||||
"An error occurred while starting the $controllerName: ${event.error ?? "unknown error"}",
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: snackbarLongDuration
|
||||
);
|
||||
break;
|
||||
case ServerResultType.stopping:
|
||||
showInfoBar(
|
||||
"Stopping the $controllerName...",
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
break;
|
||||
case ServerResultType.stopSuccess:
|
||||
if(showSuccessMessage) {
|
||||
showInfoBar(
|
||||
"The $controllerName was stopped successfully",
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
}
|
||||
completer.complete(true);
|
||||
break;
|
||||
case ServerResultType.stopError:
|
||||
showInfoBar(
|
||||
"An error occurred while stopping the $controllerName: ${event.error ?? "unknown error"}",
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: snackbarLongDuration
|
||||
);
|
||||
break;
|
||||
case ServerResultType.missingHostError:
|
||||
showInfoBar(
|
||||
"Missing hostname in $controllerName configuration",
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
case ServerResultType.missingPortError:
|
||||
showInfoBar(
|
||||
"Missing port in $controllerName configuration",
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
case ServerResultType.illegalPortError:
|
||||
showInfoBar(
|
||||
"Invalid port in $controllerName configuration",
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
case ServerResultType.freeingPort:
|
||||
showInfoBar(
|
||||
"Freeing port $defaultPort...",
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
break;
|
||||
case ServerResultType.freePortSuccess:
|
||||
showInfoBar(
|
||||
"Port $defaultPort was freed successfully",
|
||||
severity: InfoBarSeverity.success,
|
||||
duration: snackbarShortDuration
|
||||
);
|
||||
break;
|
||||
case ServerResultType.freePortError:
|
||||
showInfoBar(
|
||||
"An error occurred while freeing port $defaultPort: ${event.error ?? "unknown error"}",
|
||||
severity: InfoBarSeverity.error,
|
||||
duration: snackbarLongDuration
|
||||
);
|
||||
break;
|
||||
case ServerResultType.pingingRemote:
|
||||
if(started.value) {
|
||||
showInfoBar(
|
||||
"Pinging the remote $controllerName...",
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ServerResultType.pingingLocal:
|
||||
if(started.value) {
|
||||
showInfoBar(
|
||||
"Pinging the ${type().name} $controllerName...",
|
||||
severity: InfoBarSeverity.info,
|
||||
loading: true,
|
||||
duration: null
|
||||
);
|
||||
}
|
||||
break;
|
||||
case ServerResultType.pingError:
|
||||
showInfoBar(
|
||||
"Cannot ping ${type().name} $controllerName",
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
if(event.type.isError) {
|
||||
completer.complete(false);
|
||||
}
|
||||
});
|
||||
|
||||
var result = await completer.future;
|
||||
if(result && type() == ServerType.embedded) {
|
||||
watchProcess(embeddedServerPid!).then((value) {
|
||||
if(started()) {
|
||||
started.value = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
extension MatchmakerControllerExtension on MatchmakerController {
|
||||
Future<void> joinServer(Map<String, dynamic> entry) async {
|
||||
var hashedPassword = entry["password"];
|
||||
var hasPassword = hashedPassword != null;
|
||||
var embedded = type.value == ServerType.embedded;
|
||||
var author = entry["author"];
|
||||
var encryptedIp = entry["ip"];
|
||||
if(!hasPassword) {
|
||||
var valid = await _isServerValid(encryptedIp);
|
||||
if(!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onSuccess(embedded, encryptedIp, author);
|
||||
return;
|
||||
}
|
||||
|
||||
var confirmPassword = await _askForPassword();
|
||||
if(confirmPassword == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!checkPassword(confirmPassword, hashedPassword)) {
|
||||
showInfoBar(
|
||||
"Wrong password: please try again",
|
||||
duration: snackbarLongDuration,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var decryptedIp = aes256Decrypt(encryptedIp, confirmPassword);
|
||||
var valid = await _isServerValid(decryptedIp);
|
||||
if(!valid) {
|
||||
return;
|
||||
}
|
||||
|
||||
_onSuccess(embedded, decryptedIp, author);
|
||||
}
|
||||
|
||||
Future<bool> _isServerValid(String address) async {
|
||||
var result = await pingGameServer(address);
|
||||
if(result) {
|
||||
return true;
|
||||
}
|
||||
|
||||
showInfoBar(
|
||||
"This server isn't online right now: please try again later",
|
||||
duration: snackbarLongDuration,
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
Future<String?> _askForPassword() async {
|
||||
var confirmPasswordController = TextEditingController();
|
||||
var showPassword = RxBool(false);
|
||||
var showPasswordTrailing = RxBool(false);
|
||||
return await showAppDialog<String?>(
|
||||
builder: (context) => FormDialog(
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
InfoLabel(
|
||||
label: "Password",
|
||||
child: Obx(() => TextFormBox(
|
||||
placeholder: "Type the server's password",
|
||||
controller: confirmPasswordController,
|
||||
autovalidateMode: AutovalidateMode.always,
|
||||
obscureText: !showPassword.value,
|
||||
enableSuggestions: false,
|
||||
autofocus: true,
|
||||
autocorrect: false,
|
||||
onChanged: (text) => showPasswordTrailing.value = text.isNotEmpty,
|
||||
suffix: Button(
|
||||
onPressed: () => showPasswordTrailing.value = !showPasswordTrailing.value,
|
||||
style: ButtonStyle(
|
||||
shape: ButtonState.all(const CircleBorder()),
|
||||
backgroundColor: ButtonState.all(Colors.transparent)
|
||||
),
|
||||
child: Icon(
|
||||
showPassword.value ? Icons.visibility_off : Icons.visibility,
|
||||
color: showPassword.value ? null : Colors.transparent
|
||||
),
|
||||
)
|
||||
))
|
||||
),
|
||||
const SizedBox(height: 8.0)
|
||||
],
|
||||
),
|
||||
buttons: [
|
||||
DialogButton(
|
||||
text: "Cancel",
|
||||
type: ButtonType.secondary
|
||||
),
|
||||
|
||||
DialogButton(
|
||||
text: "Confirm",
|
||||
type: ButtonType.primary,
|
||||
onTap: () => Navigator.of(context).pop(confirmPasswordController.text)
|
||||
)
|
||||
]
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
void _onSuccess(bool embedded, String decryptedIp, String author) {
|
||||
if(embedded) {
|
||||
gameServerAddress.text = decryptedIp;
|
||||
pageIndex.value = 0;
|
||||
}else {
|
||||
FlutterClipboard.controlC(decryptedIp);
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => showInfoBar(
|
||||
embedded ? "You joined $author's server successfully!" : "Copied IP to the clipboard",
|
||||
duration: snackbarLongDuration,
|
||||
severity: InfoBarSeverity.success
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide showDialog;
|
||||
|
||||
import 'package:reboot_launcher/src/page/home_page.dart';
|
||||
import 'package:sync/semaphore.dart';
|
||||
|
||||
Semaphore _semaphore = Semaphore();
|
||||
OverlayEntry? _lastOverlay;
|
||||
|
||||
void showMessage(String text, {InfoBarSeverity severity = InfoBarSeverity.info, bool loading = false, Duration? duration = snackbarShortDuration}) {
|
||||
try {
|
||||
_semaphore.acquire();
|
||||
if(_lastOverlay?.mounted == true) {
|
||||
_lastOverlay?.remove();
|
||||
}
|
||||
var pageIndexValue = pageIndex.value;
|
||||
_lastOverlay = showSnackbar(
|
||||
pageKey.currentContext!,
|
||||
InfoBar(
|
||||
title: Text(text),
|
||||
isLong: true,
|
||||
isIconVisible: true,
|
||||
content: SizedBox(
|
||||
width: double.infinity,
|
||||
child: loading ? const ProgressBar() : const SizedBox()
|
||||
),
|
||||
severity: severity
|
||||
),
|
||||
margin: EdgeInsets.only(
|
||||
left: 330.0,
|
||||
right: 16.0,
|
||||
bottom: pageIndexValue == 0 || pageIndexValue == 1 || pageIndexValue == 3 || pageIndexValue == 4 ? 72 : 16
|
||||
),
|
||||
duration: duration
|
||||
);
|
||||
}finally {
|
||||
_semaphore.release();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user