mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
refactor
This commit is contained in:
@@ -17,7 +17,7 @@ String? _lastPort;
|
|||||||
|
|
||||||
typedef BackendErrorHandler = void Function(String);
|
typedef BackendErrorHandler = void Function(String);
|
||||||
|
|
||||||
Stream<AuthBackendResult> startAuthBackend({
|
Stream<AuthBackendEvent> startAuthBackend({
|
||||||
required AuthBackendType type,
|
required AuthBackendType type,
|
||||||
required String host,
|
required String host,
|
||||||
required String port,
|
required String port,
|
||||||
@@ -30,80 +30,80 @@ Stream<AuthBackendResult> startAuthBackend({
|
|||||||
host = host.trim();
|
host = host.trim();
|
||||||
port = port.trim();
|
port = port.trim();
|
||||||
if(type != AuthBackendType.local || port != kDefaultBackendPort.toString()) {
|
if(type != AuthBackendType.local || port != kDefaultBackendPort.toString()) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.starting);
|
yield AuthBackendEvent(AuthBackendEventType.starting);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (host.isEmpty) {
|
if (host.isEmpty) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startMissingHostError);
|
yield AuthBackendEvent(AuthBackendEventType.startMissingHostError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (port.isEmpty) {
|
if (port.isEmpty) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startMissingPortError);
|
yield AuthBackendEvent(AuthBackendEventType.startMissingPortError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final portNumber = int.tryParse(port);
|
final portNumber = int.tryParse(port);
|
||||||
if (portNumber == null) {
|
if (portNumber == null) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startIllegalPortError);
|
yield AuthBackendEvent(AuthBackendEventType.startIllegalPortError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((type != AuthBackendType.local || port != kDefaultBackendPort.toString()) && !(await isAuthBackendPortFree())) {
|
if ((type != AuthBackendType.local || port != kDefaultBackendPort.toString()) && !(await isAuthBackendPortFree())) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startFreeingPort);
|
yield AuthBackendEvent(AuthBackendEventType.startFreeingPort);
|
||||||
final result = await freeAuthBackendPort();
|
final result = await freeAuthBackendPort();
|
||||||
if(!result) {
|
if(!result) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startFreePortError);
|
yield AuthBackendEvent(AuthBackendEventType.startFreePortError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield AuthBackendResult(AuthBackendResultType.startFreePortSuccess);
|
yield AuthBackendEvent(AuthBackendEventType.startFreePortSuccess);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch(type){
|
switch(type){
|
||||||
case AuthBackendType.embedded:
|
case AuthBackendType.embedded:
|
||||||
process = await _startEmbedded(detached, onError: onError);
|
process = await _startEmbedded(detached, onError: onError);
|
||||||
yield AuthBackendResult(AuthBackendResultType.startedImplementation, implementation: AuthBackendImplementation(process: process));
|
yield AuthBackendEvent(AuthBackendEventType.startedImplementation, implementation: AuthBackendImplementation(process: process));
|
||||||
break;
|
break;
|
||||||
case AuthBackendType.remote:
|
case AuthBackendType.remote:
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingingRemote);
|
yield AuthBackendEvent(AuthBackendEventType.startPingingRemote);
|
||||||
final uriResult = await _ping(host, portNumber);
|
final uriResult = await _ping(host, portNumber);
|
||||||
if(uriResult == null) {
|
if(uriResult == null) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingError);
|
yield AuthBackendEvent(AuthBackendEventType.startPingError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
server = await _startRemote(uriResult);
|
server = await _startRemote(uriResult);
|
||||||
yield AuthBackendResult(AuthBackendResultType.startedImplementation, implementation: AuthBackendImplementation(server: server));
|
yield AuthBackendEvent(AuthBackendEventType.startedImplementation, implementation: AuthBackendImplementation(server: server));
|
||||||
break;
|
break;
|
||||||
case AuthBackendType.local:
|
case AuthBackendType.local:
|
||||||
if(portNumber != kDefaultBackendPort) {
|
if(portNumber != kDefaultBackendPort) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingingLocal);
|
yield AuthBackendEvent(AuthBackendEventType.startPingingLocal);
|
||||||
final uriResult = await _ping(kDefaultBackendHost, portNumber);
|
final uriResult = await _ping(kDefaultBackendHost, portNumber);
|
||||||
if(uriResult == null) {
|
if(uriResult == null) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingError);
|
yield AuthBackendEvent(AuthBackendEventType.startPingError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
server = await _startRemote(Uri.parse("http://$kDefaultBackendHost:$port"));
|
server = await _startRemote(Uri.parse("http://$kDefaultBackendHost:$port"));
|
||||||
yield AuthBackendResult(AuthBackendResultType.startedImplementation, implementation: AuthBackendImplementation(server: server));
|
yield AuthBackendEvent(AuthBackendEventType.startedImplementation, implementation: AuthBackendImplementation(server: server));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingingLocal);
|
yield AuthBackendEvent(AuthBackendEventType.startPingingLocal);
|
||||||
final uriResult = await _ping(kDefaultBackendHost, kDefaultBackendPort);
|
final uriResult = await _ping(kDefaultBackendHost, kDefaultBackendPort);
|
||||||
if(uriResult == null) {
|
if(uriResult == null) {
|
||||||
yield AuthBackendResult(AuthBackendResultType.startPingError);
|
yield AuthBackendEvent(AuthBackendEventType.startPingError);
|
||||||
process?.kill(ProcessSignal.sigterm);
|
process?.kill(ProcessSignal.sigterm);
|
||||||
server?.close(force: true);
|
server?.close(force: true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
yield AuthBackendResult(AuthBackendResultType.startSuccess);
|
yield AuthBackendEvent(AuthBackendEventType.startSuccess);
|
||||||
}catch(error, stackTrace) {
|
}catch(error, stackTrace) {
|
||||||
yield AuthBackendResult(
|
yield AuthBackendEvent(
|
||||||
AuthBackendResultType.startError,
|
AuthBackendEventType.startError,
|
||||||
error: error,
|
error: error,
|
||||||
stackTrace: stackTrace
|
stackTrace: stackTrace
|
||||||
);
|
);
|
||||||
@@ -141,8 +141,8 @@ Future<Process> _startEmbedded(bool detached, {BackendErrorHandler? onError}) as
|
|||||||
|
|
||||||
Future<HttpServer> _startRemote(Uri uri) async => await serve(proxyHandler(uri), kDefaultBackendHost, kDefaultBackendPort);
|
Future<HttpServer> _startRemote(Uri uri) async => await serve(proxyHandler(uri), kDefaultBackendHost, kDefaultBackendPort);
|
||||||
|
|
||||||
Stream<AuthBackendResult> stopAuthBackend({required AuthBackendType type, required AuthBackendImplementation? implementation}) async* {
|
Stream<AuthBackendEvent> stopAuthBackend({required AuthBackendType type, required AuthBackendImplementation? implementation}) async* {
|
||||||
yield AuthBackendResult(AuthBackendResultType.stopping);
|
yield AuthBackendEvent(AuthBackendEventType.stopping);
|
||||||
try{
|
try{
|
||||||
switch(type){
|
switch(type){
|
||||||
case AuthBackendType.embedded:
|
case AuthBackendType.embedded:
|
||||||
@@ -158,10 +158,10 @@ Stream<AuthBackendResult> stopAuthBackend({required AuthBackendType type, requir
|
|||||||
await implementation?.server?.close(force: true);
|
await implementation?.server?.close(force: true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
yield AuthBackendResult(AuthBackendResultType.stopSuccess);
|
yield AuthBackendEvent(AuthBackendEventType.stopSuccess);
|
||||||
}catch(error, stackTrace){
|
}catch(error, stackTrace){
|
||||||
yield AuthBackendResult(
|
yield AuthBackendEvent(
|
||||||
AuthBackendResultType.stopError,
|
AuthBackendEventType.stopError,
|
||||||
error: error,
|
error: error,
|
||||||
stackTrace: stackTrace
|
stackTrace: stackTrace
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
class AuthBackendResult {
|
class AuthBackendEvent {
|
||||||
final AuthBackendResultType type;
|
final AuthBackendEventType type;
|
||||||
final AuthBackendImplementation? implementation;
|
final AuthBackendImplementation? implementation;
|
||||||
final Object? error;
|
final Object? error;
|
||||||
final StackTrace? stackTrace;
|
final StackTrace? stackTrace;
|
||||||
|
|
||||||
AuthBackendResult(this.type, {this.implementation, this.error, this.stackTrace});
|
AuthBackendEvent(this.type, {this.implementation, this.error, this.stackTrace});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
@@ -14,14 +14,7 @@ class AuthBackendResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AuthBackendImplementation {
|
enum AuthBackendEventType {
|
||||||
final Process? process;
|
|
||||||
final HttpServer? server;
|
|
||||||
|
|
||||||
AuthBackendImplementation({this.process, this.server});
|
|
||||||
}
|
|
||||||
|
|
||||||
enum AuthBackendResultType {
|
|
||||||
starting,
|
starting,
|
||||||
startMissingHostError,
|
startMissingHostError,
|
||||||
startMissingPortError,
|
startMissingPortError,
|
||||||
@@ -43,5 +36,12 @@ enum AuthBackendResultType {
|
|||||||
|
|
||||||
bool get isError => name.endsWith("Error");
|
bool get isError => name.endsWith("Error");
|
||||||
|
|
||||||
bool get isSuccess => this == AuthBackendResultType.startSuccess || this == AuthBackendResultType.stopSuccess;
|
bool get isSuccess => this == AuthBackendEventType.startSuccess || this == AuthBackendEventType.stopSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuthBackendImplementation {
|
||||||
|
final Process? process;
|
||||||
|
final HttpServer? server;
|
||||||
|
|
||||||
|
AuthBackendImplementation({this.process, this.server});
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||||
@@ -16,7 +15,7 @@ import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/message/error.dart';
|
import 'package:reboot_launcher/src/message/error.dart';
|
||||||
import 'package:reboot_launcher/src/pager/pager.dart';
|
import 'package:reboot_launcher/src/widget/page/pager.dart';
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
import 'package:reboot_launcher/src/util/url_protocol.dart';
|
import 'package:reboot_launcher/src/util/url_protocol.dart';
|
||||||
import 'package:system_theme/system_theme.dart';
|
import 'package:system_theme/system_theme.dart';
|
||||||
|
|||||||
@@ -8,9 +8,9 @@ import 'package:get_storage/get_storage.dart';
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/main.dart';
|
import 'package:reboot_launcher/main.dart';
|
||||||
import 'package:reboot_launcher/src/util/keyboard.dart';
|
import 'package:reboot_launcher/src/util/keyboard.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
|
|
||||||
typedef BackendInteractiveEventHandler = InfoBarEntry? Function(AuthBackendType, AuthBackendResult);
|
typedef BackendInteractiveEventHandler = SnackBar? Function(AuthBackendType, AuthBackendEvent);
|
||||||
|
|
||||||
class BackendController extends GetxController {
|
class BackendController extends GetxController {
|
||||||
static const String storageName = "v3_backend_storage";
|
static const String storageName = "v3_backend_storage";
|
||||||
@@ -27,7 +27,7 @@ class BackendController extends GetxController {
|
|||||||
late final RxBool detached;
|
late final RxBool detached;
|
||||||
AuthBackendImplementation? implementation;
|
AuthBackendImplementation? implementation;
|
||||||
StreamSubscription? _worker;
|
StreamSubscription? _worker;
|
||||||
InfoBarEntry? _interactiveEntry;
|
SnackBar? _interactiveEntry;
|
||||||
|
|
||||||
BackendController() {
|
BackendController() {
|
||||||
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
_storage = appWithNoStorage ? null : GetStorage(storageName);
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ import 'package:get_storage/get_storage.dart';
|
|||||||
import 'package:path/path.dart';
|
import 'package:path/path.dart';
|
||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/main.dart';
|
import 'package:reboot_launcher/main.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/page/settings_page.dart';
|
import 'package:reboot_launcher/src/widget/page/settings/page.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
@@ -75,7 +75,7 @@ class DllController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> updateGameServerDll({bool force = false, bool silent = false}) async {
|
Future<bool> updateGameServerDll({bool force = false, bool silent = false}) async {
|
||||||
InfoBarEntry? infoBarEntry;
|
SnackBar? SnackBar;
|
||||||
try {
|
try {
|
||||||
if(customGameServer.value) {
|
if(customGameServer.value) {
|
||||||
status.value = UpdateStatus.success;
|
status.value = UpdateStatus.success;
|
||||||
@@ -94,7 +94,7 @@ class DllController extends GetxController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!silent) {
|
if(!silent) {
|
||||||
infoBarEntry = showRebootInfoBar(
|
SnackBar = SnackBar.open(
|
||||||
translations.downloadingDll("reboot"),
|
translations.downloadingDll("reboot"),
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
@@ -111,19 +111,19 @@ class DllController extends GetxController {
|
|||||||
).then((values) => values.reduce((first, second) => first && second));
|
).then((values) => values.reduce((first, second) => first && second));
|
||||||
if(!result) {
|
if(!result) {
|
||||||
status.value = UpdateStatus.error;
|
status.value = UpdateStatus.error;
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, "reboot"),
|
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, "reboot"),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
);
|
);
|
||||||
infoBarEntry?.close();
|
SnackBar?.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
timestamp.value = DateTime.now().millisecondsSinceEpoch;
|
timestamp.value = DateTime.now().millisecondsSinceEpoch;
|
||||||
status.value = UpdateStatus.success;
|
status.value = UpdateStatus.success;
|
||||||
infoBarEntry?.close();
|
SnackBar?.close();
|
||||||
if(!silent) {
|
if(!silent) {
|
||||||
infoBarEntry = showRebootInfoBar(
|
SnackBar = SnackBar.open(
|
||||||
translations.downloadDllSuccess("reboot"),
|
translations.downloadDllSuccess("reboot"),
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarShortDuration
|
duration: infoBarShortDuration
|
||||||
@@ -132,20 +132,20 @@ class DllController extends GetxController {
|
|||||||
_listenToFileEvents(GameDll.gameServer);
|
_listenToFileEvents(GameDll.gameServer);
|
||||||
return true;
|
return true;
|
||||||
}catch(message) {
|
}catch(message) {
|
||||||
infoBarEntry?.close();
|
SnackBar?.close();
|
||||||
var error = message.toString();
|
var error = message.toString();
|
||||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||||
error = error.toLowerCase();
|
error = error.toLowerCase();
|
||||||
status.value = UpdateStatus.error;
|
status.value = UpdateStatus.error;
|
||||||
final completer = Completer<bool>();
|
final completer = Completer<bool>();
|
||||||
infoBarEntry = showRebootInfoBar(
|
SnackBar = SnackBar.open(
|
||||||
translations.downloadDllError(error.toString(), "reboot.dll"),
|
translations.downloadDllError(error.toString(), "reboot.dll"),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
onDismissed: () => completer.complete(false),
|
onDismissed: () => completer.complete(false),
|
||||||
action: Button(
|
action: Button(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
infoBarEntry?.close();
|
SnackBar?.close();
|
||||||
final result = updateGameServerDll(
|
final result = updateGameServerDll(
|
||||||
force: true,
|
force: true,
|
||||||
silent: silent
|
silent: silent
|
||||||
@@ -216,7 +216,7 @@ class DllController extends GetxController {
|
|||||||
|
|
||||||
Future<bool> download(GameDll dll, String filePath, {bool silent = false, bool force = false}) async {
|
Future<bool> download(GameDll dll, String filePath, {bool silent = false, bool force = false}) async {
|
||||||
log("[DLL] Asking for $dll at $filePath(silent: $silent, force: $force)");
|
log("[DLL] Asking for $dll at $filePath(silent: $silent, force: $force)");
|
||||||
InfoBarEntry? entry;
|
SnackBar? entry;
|
||||||
try {
|
try {
|
||||||
if (dll == GameDll.gameServer) {
|
if (dll == GameDll.gameServer) {
|
||||||
return await updateGameServerDll(silent: silent);
|
return await updateGameServerDll(silent: silent);
|
||||||
@@ -232,7 +232,7 @@ class DllController extends GetxController {
|
|||||||
final fileNameWithoutExtension = basenameWithoutExtension(filePath);
|
final fileNameWithoutExtension = basenameWithoutExtension(filePath);
|
||||||
if(!silent) {
|
if(!silent) {
|
||||||
log("[DLL] Showing dialog while downloading $dll...");
|
log("[DLL] Showing dialog while downloading $dll...");
|
||||||
entry = showRebootInfoBar(
|
entry = SnackBar.open(
|
||||||
translations.downloadingDll(fileNameWithoutExtension),
|
translations.downloadingDll(fileNameWithoutExtension),
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
@@ -243,7 +243,7 @@ class DllController extends GetxController {
|
|||||||
final result = await downloadDependency(dll, filePath);
|
final result = await downloadDependency(dll, filePath);
|
||||||
if(!result) {
|
if(!result) {
|
||||||
entry?.close();
|
entry?.close();
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, dll.name),
|
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, dll.name),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -254,7 +254,7 @@ class DllController extends GetxController {
|
|||||||
entry?.close();
|
entry?.close();
|
||||||
if(!silent) {
|
if(!silent) {
|
||||||
log("[DLL] Showing success dialog for $dll");
|
log("[DLL] Showing success dialog for $dll");
|
||||||
entry = await showRebootInfoBar(
|
entry = await SnackBar.open(
|
||||||
translations.downloadDllSuccess(fileNameWithoutExtension),
|
translations.downloadDllSuccess(fileNameWithoutExtension),
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarShortDuration
|
duration: infoBarShortDuration
|
||||||
@@ -271,7 +271,7 @@ class DllController extends GetxController {
|
|||||||
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
error = error.contains(": ") ? error.substring(error.indexOf(": ") + 2) : error;
|
||||||
error = error.toLowerCase();
|
error = error.toLowerCase();
|
||||||
final completer = Completer<bool>();
|
final completer = Completer<bool>();
|
||||||
await showRebootInfoBar(
|
await SnackBar.open(
|
||||||
translations.downloadDllError(error.toString(), dll.name),
|
translations.downloadDllError(error.toString(), dll.name),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
@@ -317,7 +317,7 @@ class DllController extends GetxController {
|
|||||||
.instance
|
.instance
|
||||||
.value
|
.value
|
||||||
?.kill();
|
?.kill();
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, injectable.name),
|
translations.downloadDllAntivirus(antiVirusName ?? defaultAntiVirusName, injectable.name),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import 'package:http/http.dart' as http;
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/main.dart';
|
import 'package:reboot_launcher/main.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
import 'package:yaml/yaml.dart';
|
import 'package:yaml/yaml.dart';
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
|
|
||||||
Future<void> showResetDialog(Function() onConfirm) => showRebootDialog(
|
Future<void> showResetDialog(Function() onConfirm) => showRebootDialog(
|
||||||
builder: (context) => InfoDialog(
|
builder: (context) => InfoDialog(
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
|
|
||||||
Future<void> showDllDeletedDialog() => showRebootDialog(
|
Future<void> showDllDeletedDialog() => showRebootDialog(
|
||||||
builder: (context) => InfoDialog(
|
builder: (context) => InfoDialog(
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import 'package:reboot_launcher/src/controller/game_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/util/extensions.dart';
|
import 'package:reboot_launcher/src/util/extensions.dart';
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/button/file_selector.dart';
|
import 'package:reboot_launcher/src/widget/file_selector.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
import 'package:windows_taskbar/windows_taskbar.dart';
|
import 'package:windows_taskbar/windows_taskbar.dart';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
|
|
||||||
String? lastError;
|
String? lastError;
|
||||||
|
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import 'package:path/path.dart' as path;
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/button/file_selector.dart';
|
import 'package:reboot_launcher/src/widget/file_selector.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
|
|
||||||
class ImportVersionDialog extends StatefulWidget {
|
class ImportVersionDialog extends StatefulWidget {
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/message/profile.dart';
|
import 'package:reboot_launcher/src/message/profile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/page/backend_page.dart';
|
import 'package:reboot_launcher/src/widget/section/backend/page.dart';
|
||||||
import 'package:reboot_launcher/src/pager/pager.dart';
|
import 'package:reboot_launcher/src/widget/page/pager.dart';
|
||||||
import 'package:reboot_launcher/src/page/host_page.dart';
|
import 'package:reboot_launcher/src/widget/page/host/page.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
import 'package:reboot_launcher/src/page/play_page.dart';
|
import 'package:reboot_launcher/src/widget/section/play/page.dart';
|
||||||
import 'package:reboot_launcher/src/button/version_selector.dart';
|
import 'package:reboot_launcher/src/widget/version_selector.dart';
|
||||||
|
|
||||||
void startOnboarding() {
|
void startOnboarding() {
|
||||||
final gameController = Get.find<GameController>();
|
final gameController = Get.find<GameController>();
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import 'package:fluent_ui/fluent_ui.dart';
|
|||||||
import 'package:flutter/material.dart' show Icons;
|
import 'package:flutter/material.dart' show Icons;
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
|
|
||||||
Future<bool> showProfileForm(BuildContext context, TextEditingController username, TextEditingController password) async{
|
Future<bool> showProfileForm(BuildContext context, TextEditingController username, TextEditingController password) async{
|
||||||
final showPassword = RxBool(false);
|
final showPassword = RxBool(false);
|
||||||
|
|||||||
@@ -1,325 +0,0 @@
|
|||||||
import 'package:clipboard/clipboard.dart';
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as fluent show showDialog;
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
|
||||||
|
|
||||||
import 'info_bar.dart';
|
|
||||||
|
|
||||||
bool inDialog = false;
|
|
||||||
|
|
||||||
Future<T?> showRebootDialog<T extends Object?>({required WidgetBuilder builder, bool dismissWithEsc = true}) async {
|
|
||||||
inDialog = true;
|
|
||||||
pagesController.add(null);
|
|
||||||
try {
|
|
||||||
return await fluent.showDialog(
|
|
||||||
context: appNavigatorKey.currentContext!,
|
|
||||||
useRootNavigator: false,
|
|
||||||
dismissWithEsc: dismissWithEsc,
|
|
||||||
builder: builder
|
|
||||||
);
|
|
||||||
}finally {
|
|
||||||
inDialog = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class AbstractDialog extends StatelessWidget {
|
|
||||||
const AbstractDialog({Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context);
|
|
||||||
}
|
|
||||||
|
|
||||||
class GenericDialog extends AbstractDialog {
|
|
||||||
final Widget header;
|
|
||||||
final List<DialogButton> buttons;
|
|
||||||
final EdgeInsets? padding;
|
|
||||||
|
|
||||||
const GenericDialog({Key? key, required this.header, required this.buttons, this.padding}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => ContentDialog(
|
|
||||||
style: ContentDialogThemeData(
|
|
||||||
padding: padding ?? const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 5.0)
|
|
||||||
),
|
|
||||||
content: header,
|
|
||||||
actions: buttons
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class FormDialog extends AbstractDialog {
|
|
||||||
final Widget content;
|
|
||||||
final List<DialogButton> buttons;
|
|
||||||
|
|
||||||
const FormDialog({Key? key, required this.content, required this.buttons}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Form(
|
|
||||||
child: Builder(
|
|
||||||
builder: (context) {
|
|
||||||
final parsed = buttons.map((entry) => _createFormButton(entry, context)).toList();
|
|
||||||
return GenericDialog(
|
|
||||||
header: content,
|
|
||||||
buttons: parsed
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogButton _createFormButton(DialogButton entry, BuildContext context) {
|
|
||||||
if (entry.type != ButtonType.primary) {
|
|
||||||
return entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
return DialogButton(
|
|
||||||
text: entry.text,
|
|
||||||
type: entry.type,
|
|
||||||
onTap: () {
|
|
||||||
if(!Form.of(context).validate()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.onTap?.call();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class InfoDialog extends AbstractDialog {
|
|
||||||
final String text;
|
|
||||||
final List<DialogButton>? buttons;
|
|
||||||
|
|
||||||
const InfoDialog({required this.text, this.buttons, Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
InfoDialog.ofOnly({required this.text, required DialogButton button, Key? key})
|
|
||||||
: buttons = [button], super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GenericDialog(
|
|
||||||
header: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: Text(text, textAlign: TextAlign.center)
|
|
||||||
),
|
|
||||||
buttons: buttons ?? [_defaultCloseButton],
|
|
||||||
padding: const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 15.0)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogButton get _defaultCloseButton =>DialogButton(
|
|
||||||
text: translations.defaultDialogSecondaryAction,
|
|
||||||
type: ButtonType.only
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
class ProgressDialog extends AbstractDialog {
|
|
||||||
final String text;
|
|
||||||
final Function()? onStop;
|
|
||||||
final bool showButton;
|
|
||||||
|
|
||||||
const ProgressDialog({required this.text, this.onStop, this.showButton = true, Key? key}) : super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return GenericDialog(
|
|
||||||
header: InfoLabel(
|
|
||||||
label: text,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
|
||||||
width: double.infinity,
|
|
||||||
child: const ProgressBar()
|
|
||||||
),
|
|
||||||
),
|
|
||||||
buttons: [
|
|
||||||
if(showButton)
|
|
||||||
DialogButton(
|
|
||||||
text: translations.defaultDialogSecondaryAction,
|
|
||||||
type: ButtonType.only,
|
|
||||||
onTap: onStop
|
|
||||||
)
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FutureBuilderDialog extends AbstractDialog {
|
|
||||||
final Future future;
|
|
||||||
final String loadingMessage;
|
|
||||||
final Widget successfulBody;
|
|
||||||
final Widget unsuccessfulBody;
|
|
||||||
final Function(Object) errorMessageBuilder;
|
|
||||||
final Function()? onError;
|
|
||||||
final bool closeAutomatically;
|
|
||||||
|
|
||||||
const FutureBuilderDialog(
|
|
||||||
{Key? key,
|
|
||||||
required this.future,
|
|
||||||
required this.loadingMessage,
|
|
||||||
required this.successfulBody,
|
|
||||||
required this.unsuccessfulBody,
|
|
||||||
required this.errorMessageBuilder,
|
|
||||||
this.onError,
|
|
||||||
this.closeAutomatically = false}) : super(key: key);
|
|
||||||
|
|
||||||
static Container ofMessage(String message) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
padding: const EdgeInsets.only(bottom: 16.0),
|
|
||||||
child: Text(
|
|
||||||
message,
|
|
||||||
textAlign: TextAlign.center
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return FutureBuilder(
|
|
||||||
future: future,
|
|
||||||
builder: (context, snapshot) => GenericDialog(
|
|
||||||
header: _createBody(context, snapshot),
|
|
||||||
buttons: [_createButton(context, snapshot)]
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _createBody(BuildContext context, AsyncSnapshot snapshot){
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
onError?.call();
|
|
||||||
return ofMessage(errorMessageBuilder(snapshot.error!));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(snapshot.connectionState == ConnectionState.done && (snapshot.data == null || (snapshot.data is bool && !snapshot.data))){
|
|
||||||
return unsuccessfulBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!snapshot.hasData) {
|
|
||||||
return _createLoadingBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(closeAutomatically){
|
|
||||||
WidgetsBinding.instance
|
|
||||||
.addPostFrameCallback((_) => Navigator.of(context).pop(true));
|
|
||||||
return _createLoadingBody();
|
|
||||||
}
|
|
||||||
|
|
||||||
return successfulBody;
|
|
||||||
}
|
|
||||||
|
|
||||||
InfoLabel _createLoadingBody() {
|
|
||||||
return InfoLabel(
|
|
||||||
label: loadingMessage,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.only(bottom: 16.0),
|
|
||||||
width: double.infinity,
|
|
||||||
child: const ProgressBar()),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
DialogButton _createButton(BuildContext context, AsyncSnapshot snapshot){
|
|
||||||
return DialogButton(
|
|
||||||
text: snapshot.hasData
|
|
||||||
|| snapshot.hasError
|
|
||||||
|| (snapshot.connectionState == ConnectionState.done && snapshot.data == null) ? translations.defaultDialogSecondaryAction : translations.stopLoadingDialogAction,
|
|
||||||
type: ButtonType.only,
|
|
||||||
onTap: () => Navigator.of(context).pop(!snapshot.hasError && snapshot.hasData)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ErrorDialog extends AbstractDialog {
|
|
||||||
final Object exception;
|
|
||||||
final StackTrace? stackTrace;
|
|
||||||
final Function(Object) errorMessageBuilder;
|
|
||||||
|
|
||||||
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: translations.copyErrorDialogTitle,
|
|
||||||
type: type,
|
|
||||||
onTap: () async {
|
|
||||||
FlutterClipboard.controlC("$error\n$stackTrace");
|
|
||||||
showRebootInfoBar(translations.copyErrorDialogSuccess);
|
|
||||||
onClick();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return InfoDialog(
|
|
||||||
text: errorMessageBuilder(exception),
|
|
||||||
buttons: [
|
|
||||||
DialogButton(
|
|
||||||
type: stackTrace == null ? ButtonType.only : ButtonType.secondary
|
|
||||||
),
|
|
||||||
|
|
||||||
if(stackTrace != null)
|
|
||||||
createCopyErrorButton(
|
|
||||||
error: exception,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
onClick: () => Navigator.pop(context)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DialogButton extends StatefulWidget {
|
|
||||||
final String? text;
|
|
||||||
final Function()? onTap;
|
|
||||||
final ButtonType type;
|
|
||||||
final Color? color;
|
|
||||||
|
|
||||||
const DialogButton(
|
|
||||||
{Key? key,
|
|
||||||
this.text,
|
|
||||||
this.onTap,
|
|
||||||
this.color,
|
|
||||||
required this.type})
|
|
||||||
: assert(type != ButtonType.primary || onTap != null,
|
|
||||||
"OnTap handler cannot be null for primary buttons"),
|
|
||||||
assert(type != ButtonType.primary || text != null,
|
|
||||||
"Text cannot be null for primary buttons"),
|
|
||||||
super(key: key);
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<DialogButton> createState() => _DialogButtonState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _DialogButtonState extends State<DialogButton> {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => widget.type == ButtonType.only ? _onlyButton : _button;
|
|
||||||
|
|
||||||
SizedBox get _onlyButton => SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: _button
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget get _button => widget.type == ButtonType.primary ? _primaryButton : _secondaryButton;
|
|
||||||
|
|
||||||
Widget get _primaryButton => Button(
|
|
||||||
style: ButtonStyle(
|
|
||||||
backgroundColor: WidgetStateProperty.all(FluentTheme.of(context).accentColor)
|
|
||||||
),
|
|
||||||
onPressed: widget.onTap!,
|
|
||||||
child: Text(widget.text!),
|
|
||||||
);
|
|
||||||
|
|
||||||
Widget get _secondaryButton => Button(
|
|
||||||
style: widget.color != null ? ButtonStyle(
|
|
||||||
backgroundColor: WidgetStateProperty.all(widget.color!)
|
|
||||||
) : null,
|
|
||||||
onPressed: widget.onTap ?? _onDefaultSecondaryActionTap,
|
|
||||||
child: Text(widget.text ?? translations.defaultDialogSecondaryAction),
|
|
||||||
);
|
|
||||||
|
|
||||||
void _onDefaultSecondaryActionTap() => Navigator.of(context).pop(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum ButtonType {
|
|
||||||
primary,
|
|
||||||
secondary,
|
|
||||||
only
|
|
||||||
}
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
|
||||||
|
|
||||||
const infoBarLongDuration = Duration(seconds: 4);
|
|
||||||
const infoBarShortDuration = Duration(seconds: 2);
|
|
||||||
const _height = 64.0;
|
|
||||||
|
|
||||||
InfoBarEntry showRebootInfoBar(String text, {
|
|
||||||
InfoBarSeverity severity = InfoBarSeverity.info,
|
|
||||||
bool loading = false,
|
|
||||||
Duration? duration = infoBarShortDuration,
|
|
||||||
void Function()? onDismissed,
|
|
||||||
Widget? action
|
|
||||||
}) {
|
|
||||||
final overlay = _buildOverlay(text, action, loading, severity);
|
|
||||||
final overlayEntry = InfoBarEntry(overlay: overlay, onDismissed: onDismissed);
|
|
||||||
if(duration != null) {
|
|
||||||
Future.delayed(duration)
|
|
||||||
.then((_) => WidgetsBinding.instance.addPostFrameCallback((timeStamp) => overlayEntry.close()));
|
|
||||||
}
|
|
||||||
return overlayEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _buildOverlay(text, Widget? action, bool loading, InfoBarSeverity severity) => ConstrainedBox(
|
|
||||||
constraints: BoxConstraints(
|
|
||||||
minHeight: _height
|
|
||||||
),
|
|
||||||
child: Mica(
|
|
||||||
elevation: 1,
|
|
||||||
child: InfoBar(
|
|
||||||
title: Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: text is Widget ? text : Text(text)
|
|
||||||
),
|
|
||||||
if(action != null)
|
|
||||||
action
|
|
||||||
],
|
|
||||||
),
|
|
||||||
isLong: false,
|
|
||||||
isIconVisible: true,
|
|
||||||
content: SizedBox(
|
|
||||||
width: double.infinity,
|
|
||||||
child: loading ? const Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 8.0,
|
|
||||||
bottom: 2.0,
|
|
||||||
right: 6.0
|
|
||||||
),
|
|
||||||
child: ProgressBar(),
|
|
||||||
) : const SizedBox()
|
|
||||||
),
|
|
||||||
severity: severity
|
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
class InfoBarEntry {
|
|
||||||
final Widget overlay;
|
|
||||||
final void Function()? onDismissed;
|
|
||||||
|
|
||||||
InfoBarEntry({required this.overlay, required this.onDismissed}) {
|
|
||||||
final context = pageKey.currentContext;
|
|
||||||
if(context != null) {
|
|
||||||
infoBarAreaKey.currentState?.insertChild(overlay);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool close() {
|
|
||||||
final result = infoBarAreaKey.currentState?.removeChild(overlay) ?? false;
|
|
||||||
if(result) {
|
|
||||||
onDismissed?.call();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
|
||||||
|
|
||||||
class InfoBarArea extends StatefulWidget {
|
|
||||||
const InfoBarArea({super.key});
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<InfoBarArea> createState() => InfoBarAreaState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class InfoBarAreaState extends State<InfoBarArea> {
|
|
||||||
final Rx<List<Widget>> _children = Rx([]);
|
|
||||||
|
|
||||||
void insertChild(Widget child) {
|
|
||||||
_children.value.add(child);
|
|
||||||
_children.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool removeChild(Widget child) {
|
|
||||||
final result = _children.value.remove(child);
|
|
||||||
_children.refresh();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => StreamBuilder(
|
|
||||||
stream: pagesController.stream,
|
|
||||||
builder: (context, _) => Obx(() => Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: hasPageButton ? 72.0 : 16.0
|
|
||||||
),
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: _children.value.map((child) => Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 12.0
|
|
||||||
),
|
|
||||||
child: child
|
|
||||||
)).toList(growable: false)
|
|
||||||
),
|
|
||||||
))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
import 'dart:io';
|
|
||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart' as fluentIcons show FluentIcons;
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:get/get.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
|
||||||
import 'package:reboot_launcher/src/button/file_selector.dart';
|
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
|
||||||
|
|
||||||
const double _kButtonDimensions = 30;
|
|
||||||
const double _kButtonSpacing = 8;
|
|
||||||
|
|
||||||
SettingTile createFileSetting({
|
|
||||||
required GlobalKey<TextFormBoxState> key,
|
|
||||||
required String title,
|
|
||||||
required String description,
|
|
||||||
required TextEditingController controller,
|
|
||||||
required void Function() onReset
|
|
||||||
}) {
|
|
||||||
final obx = RxnString();
|
|
||||||
final selecting = RxBool(false);
|
|
||||||
return SettingTile(
|
|
||||||
icon: Icon(
|
|
||||||
FluentIcons.document_24_regular
|
|
||||||
),
|
|
||||||
title: Text(title),
|
|
||||||
subtitle: Text(description),
|
|
||||||
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions,
|
|
||||||
content: Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: FileSelector(
|
|
||||||
placeholder: translations.selectPathPlaceholder,
|
|
||||||
windowTitle: translations.selectPathWindowTitle,
|
|
||||||
controller: controller,
|
|
||||||
validator: (text) {
|
|
||||||
final result = _checkDll(text);
|
|
||||||
obx.value = result;
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
extension: "dll",
|
|
||||||
folder: false,
|
|
||||||
validatorMode: AutovalidateMode.always,
|
|
||||||
allowNavigator: false,
|
|
||||||
validatorKey: key
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: _kButtonSpacing),
|
|
||||||
Obx(() => Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: obx.value == null ? 0.0 : 20.0
|
|
||||||
),
|
|
||||||
child: Tooltip(
|
|
||||||
message: translations.selectFile,
|
|
||||||
child: Button(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
|
||||||
),
|
|
||||||
onPressed: () => _onPressed(selecting, controller),
|
|
||||||
child: SizedBox.square(
|
|
||||||
dimension: _kButtonDimensions,
|
|
||||||
child: Icon(
|
|
||||||
fluentIcons.FluentIcons.open_folder_horizontal
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
const SizedBox(width: _kButtonSpacing),
|
|
||||||
Obx(() => Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
bottom: obx.value == null ? 0.0 : 20.0
|
|
||||||
),
|
|
||||||
child: Tooltip(
|
|
||||||
message: translations.reset,
|
|
||||||
child: Button(
|
|
||||||
style: ButtonStyle(
|
|
||||||
padding: WidgetStateProperty.all(EdgeInsets.zero)
|
|
||||||
),
|
|
||||||
onPressed: onReset,
|
|
||||||
child: SizedBox.square(
|
|
||||||
dimension: _kButtonDimensions,
|
|
||||||
child: Icon(
|
|
||||||
FluentIcons.arrow_reset_24_regular
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _onPressed(RxBool selecting, TextEditingController controller) {
|
|
||||||
if(selecting.value){
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selecting.value = true;
|
|
||||||
compute(openFilePicker, "dll")
|
|
||||||
.then((value) => _updateText(controller, value))
|
|
||||||
.then((_) => selecting.value = false);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateText(TextEditingController controller, String? value) {
|
|
||||||
final text = value ?? controller.text;
|
|
||||||
controller.text = text;
|
|
||||||
controller.selection = TextSelection.collapsed(offset: text.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
String? _checkDll(String? text) {
|
|
||||||
if (text == null || text.isEmpty) {
|
|
||||||
return translations.invalidDllPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
final file = File(text);
|
|
||||||
try {
|
|
||||||
file.readAsBytesSync();
|
|
||||||
}catch(_) {
|
|
||||||
return translations.dllDoesNotExist;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!text.endsWith(".dll")) {
|
|
||||||
return translations.invalidDllExtension;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
|
||||||
|
|
||||||
class InfoTile extends StatelessWidget {
|
|
||||||
final Key? expanderKey;
|
|
||||||
final Text title;
|
|
||||||
final Text content;
|
|
||||||
|
|
||||||
const InfoTile({
|
|
||||||
this.expanderKey,
|
|
||||||
required this.title,
|
|
||||||
required this.content
|
|
||||||
});
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) => Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
bottom: 4.0
|
|
||||||
),
|
|
||||||
child: Expander(
|
|
||||||
key: expanderKey,
|
|
||||||
header: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
FluentIcons.info_24_regular
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16.0),
|
|
||||||
title
|
|
||||||
],
|
|
||||||
),
|
|
||||||
content: content,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -53,7 +53,9 @@ String _sanitize(String value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
List<String> _getArguments(List<String>? arguments) {
|
List<String> _getArguments(List<String>? arguments) {
|
||||||
if (arguments == null) return ['%s'];
|
if (arguments == null) {
|
||||||
|
return ['%s'];
|
||||||
|
}
|
||||||
|
|
||||||
if (arguments.isEmpty && !arguments.any((e) => e.contains('%s'))) {
|
if (arguments.isEmpty && !arguments.any((e) => e.contains('%s'))) {
|
||||||
throw ArgumentError('arguments must contain at least 1 instance of "%s"');
|
throw ArgumentError('arguments must contain at least 1 instance of "%s"');
|
||||||
|
|||||||
20
gui/lib/src/widget/dialog/dialog.dart
Normal file
20
gui/lib/src/widget/dialog/dialog.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart' as fluent show showDialog;
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/sections.dart';
|
||||||
|
|
||||||
|
abstract class Dialog extends StatelessWidget {
|
||||||
|
static Future<T?> open<T extends Object?>({
|
||||||
|
required WidgetBuilder builder,
|
||||||
|
bool dismissWithEsc = true
|
||||||
|
}) => fluent.showDialog(
|
||||||
|
context: appNavigatorKey.currentContext!,
|
||||||
|
useRootNavigator: false,
|
||||||
|
dismissWithEsc: dismissWithEsc,
|
||||||
|
builder: builder
|
||||||
|
);
|
||||||
|
|
||||||
|
const Dialog({Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context);
|
||||||
|
}
|
||||||
58
gui/lib/src/widget/dialog/dialog_button.dart
Normal file
58
gui/lib/src/widget/dialog/dialog_button.dart
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
|
||||||
|
class DialogButton extends StatefulWidget {
|
||||||
|
final String? text;
|
||||||
|
final Function()? onTap;
|
||||||
|
final DialogButtonType type;
|
||||||
|
final Color? color;
|
||||||
|
|
||||||
|
const DialogButton({
|
||||||
|
Key? key,
|
||||||
|
this.text,
|
||||||
|
this.onTap,
|
||||||
|
this.color,
|
||||||
|
required this.type
|
||||||
|
}): assert(type != DialogButtonType.primary || onTap != null, "OnTap handler cannot be null for primary buttons"),
|
||||||
|
assert(type != DialogButtonType.primary || text != null, "Text cannot be null for primary buttons"),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DialogButton> createState() => _DialogButtonState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DialogButtonState extends State<DialogButton> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => widget.type == DialogButtonType.only ? _onlyButton : _button;
|
||||||
|
|
||||||
|
SizedBox get _onlyButton => SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: _button
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget get _button => widget.type == DialogButtonType.primary ? _primaryButton : _secondaryButton;
|
||||||
|
|
||||||
|
Widget get _primaryButton => Button(
|
||||||
|
style: ButtonStyle(
|
||||||
|
backgroundColor: WidgetStateProperty.all(FluentTheme.of(context).accentColor)
|
||||||
|
),
|
||||||
|
onPressed: widget.onTap!,
|
||||||
|
child: Text(widget.text!),
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget get _secondaryButton => Button(
|
||||||
|
style: widget.color != null ? ButtonStyle(
|
||||||
|
backgroundColor: WidgetStateProperty.all(widget.color!)
|
||||||
|
) : null,
|
||||||
|
onPressed: widget.onTap ?? _onDefaultSecondaryActionTap,
|
||||||
|
child: Text(widget.text ?? translations.defaultDialogSecondaryAction),
|
||||||
|
);
|
||||||
|
|
||||||
|
void _onDefaultSecondaryActionTap() => Navigator.of(context).pop(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
enum DialogButtonType {
|
||||||
|
primary,
|
||||||
|
secondary,
|
||||||
|
only
|
||||||
|
}
|
||||||
39
gui/lib/src/widget/dialog/error_dialog.dart
Normal file
39
gui/lib/src/widget/dialog/error_dialog.dart
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import 'package:clipboard/clipboard.dart';
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
|
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
import 'info_dialog.dart';
|
||||||
|
|
||||||
|
class ErrorDialog extends Dialog {
|
||||||
|
final Object exception;
|
||||||
|
final StackTrace? stackTrace;
|
||||||
|
final Function(Object) errorMessageBuilder;
|
||||||
|
|
||||||
|
const ErrorDialog({Key? key, required this.exception, required this.errorMessageBuilder, this.stackTrace}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return InfoDialog(
|
||||||
|
text: errorMessageBuilder(exception),
|
||||||
|
buttons: [
|
||||||
|
DialogButton(
|
||||||
|
type: stackTrace == null ? DialogButtonType.only : DialogButtonType.secondary
|
||||||
|
),
|
||||||
|
|
||||||
|
if(stackTrace != null)
|
||||||
|
DialogButton(
|
||||||
|
text: translations.copyErrorDialogTitle,
|
||||||
|
type: DialogButtonType.primary,
|
||||||
|
onTap: () async {
|
||||||
|
FlutterClipboard.controlC("$exception\n$stackTrace");
|
||||||
|
SnackBar.open(translations.copyErrorDialogSuccess);
|
||||||
|
Navigator.pop(context);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
gui/lib/src/widget/dialog/form_dialog.dart
Normal file
40
gui/lib/src/widget/dialog/form_dialog.dart
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
import 'generic_dialog.dart';
|
||||||
|
|
||||||
|
class FormDialog extends Dialog {
|
||||||
|
final Widget content;
|
||||||
|
final List<DialogButton> buttons;
|
||||||
|
|
||||||
|
const FormDialog({Key? key, required this.content, required this.buttons}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => Form(
|
||||||
|
child: Builder(
|
||||||
|
builder: (context) => GenericDialog(
|
||||||
|
header: content,
|
||||||
|
buttons: buttons.map((entry) => _createFormButton(entry, context)).toList()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
DialogButton _createFormButton(DialogButton entry, BuildContext context) {
|
||||||
|
if (entry.type != DialogButtonType.primary) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DialogButton(
|
||||||
|
text: entry.text,
|
||||||
|
type: entry.type,
|
||||||
|
onTap: () {
|
||||||
|
if(!Form.of(context).validate()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entry.onTap?.call();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
gui/lib/src/widget/dialog/future_dialog.dart
Normal file
88
gui/lib/src/widget/dialog/future_dialog.dart
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
import 'generic_dialog.dart';
|
||||||
|
|
||||||
|
class FutureDialog extends Dialog {
|
||||||
|
final Future future;
|
||||||
|
final String loadingMessage;
|
||||||
|
final Widget successfulBody;
|
||||||
|
final Widget unsuccessfulBody;
|
||||||
|
final Function(Object) errorMessageBuilder;
|
||||||
|
final Function()? onError;
|
||||||
|
final bool closeAutomatically;
|
||||||
|
|
||||||
|
const FutureDialog(
|
||||||
|
{Key? key,
|
||||||
|
required this.future,
|
||||||
|
required this.loadingMessage,
|
||||||
|
required this.successfulBody,
|
||||||
|
required this.unsuccessfulBody,
|
||||||
|
required this.errorMessageBuilder,
|
||||||
|
this.onError,
|
||||||
|
this.closeAutomatically = false}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return FutureBuilder(
|
||||||
|
future: future,
|
||||||
|
builder: (context, snapshot) => GenericDialog(
|
||||||
|
header: _createBody(context, snapshot),
|
||||||
|
buttons: [_createButton(context, snapshot)]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _createBody(BuildContext context, AsyncSnapshot snapshot){
|
||||||
|
if (snapshot.hasError) {
|
||||||
|
onError?.call();
|
||||||
|
return _buildData(errorMessageBuilder(snapshot.error!));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(snapshot.connectionState == ConnectionState.done
|
||||||
|
&& (snapshot.data == null || (snapshot.data is bool && !snapshot.data))){
|
||||||
|
return unsuccessfulBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!snapshot.hasData) {
|
||||||
|
return _loadingBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(closeAutomatically){
|
||||||
|
WidgetsBinding.instance
|
||||||
|
.addPostFrameCallback((_) => Navigator.of(context).pop(true));
|
||||||
|
return _loadingBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
return successfulBody;
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildData(String message) {
|
||||||
|
return Container(
|
||||||
|
width: double.infinity,
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
child: Text(
|
||||||
|
message,
|
||||||
|
textAlign: TextAlign.center
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget get _loadingBody => InfoLabel(
|
||||||
|
label: loadingMessage,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
|
width: double.infinity,
|
||||||
|
child: const ProgressBar()),
|
||||||
|
);
|
||||||
|
|
||||||
|
DialogButton _createButton(BuildContext context, AsyncSnapshot snapshot)=> DialogButton(
|
||||||
|
text: snapshot.hasData
|
||||||
|
|| snapshot.hasError
|
||||||
|
|| (snapshot.connectionState == ConnectionState.done && snapshot.data == null) ? translations.defaultDialogSecondaryAction : translations.stopLoadingDialogAction,
|
||||||
|
type: DialogButtonType.only,
|
||||||
|
onTap: () => Navigator.of(context).pop(!snapshot.hasError && snapshot.hasData)
|
||||||
|
);
|
||||||
|
}
|
||||||
21
gui/lib/src/widget/dialog/generic_dialog.dart
Normal file
21
gui/lib/src/widget/dialog/generic_dialog.dart
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
|
||||||
|
class GenericDialog extends Dialog {
|
||||||
|
final Widget header;
|
||||||
|
final List<DialogButton> buttons;
|
||||||
|
final EdgeInsets? padding;
|
||||||
|
|
||||||
|
const GenericDialog({Key? key, required this.header, required this.buttons, this.padding}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) => ContentDialog(
|
||||||
|
style: ContentDialogThemeData(
|
||||||
|
padding: padding ?? const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 5.0)
|
||||||
|
),
|
||||||
|
content: header,
|
||||||
|
actions: buttons
|
||||||
|
);
|
||||||
|
}
|
||||||
33
gui/lib/src/widget/dialog/info_dialog.dart
Normal file
33
gui/lib/src/widget/dialog/info_dialog.dart
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
import 'generic_dialog.dart';
|
||||||
|
|
||||||
|
class InfoDialog extends Dialog {
|
||||||
|
final String text;
|
||||||
|
final List<DialogButton>? buttons;
|
||||||
|
|
||||||
|
const InfoDialog({required this.text, this.buttons, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
InfoDialog.ofOnly({required this.text, required DialogButton button, Key? key})
|
||||||
|
: buttons = [button], super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GenericDialog(
|
||||||
|
header: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: Text(text, textAlign: TextAlign.center)
|
||||||
|
),
|
||||||
|
buttons: buttons ?? [_defaultCloseButton],
|
||||||
|
padding: const EdgeInsets.only(left: 20, right: 20, top: 15.0, bottom: 15.0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
DialogButton get _defaultCloseButton =>DialogButton(
|
||||||
|
text: translations.defaultDialogSecondaryAction,
|
||||||
|
type: DialogButtonType.only
|
||||||
|
);
|
||||||
|
}
|
||||||
36
gui/lib/src/widget/dialog/progress_dialog.dart
Normal file
36
gui/lib/src/widget/dialog/progress_dialog.dart
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
import 'dialog.dart';
|
||||||
|
import 'dialog_button.dart';
|
||||||
|
import 'generic_dialog.dart';
|
||||||
|
|
||||||
|
class ProgressDialog extends Dialog {
|
||||||
|
final String text;
|
||||||
|
final Function()? onStop;
|
||||||
|
final bool showButton;
|
||||||
|
|
||||||
|
const ProgressDialog({required this.text, this.onStop, this.showButton = true, Key? key}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return GenericDialog(
|
||||||
|
header: InfoLabel(
|
||||||
|
label: text,
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 16.0),
|
||||||
|
width: double.infinity,
|
||||||
|
child: const ProgressBar()
|
||||||
|
),
|
||||||
|
),
|
||||||
|
buttons: [
|
||||||
|
if(showButton)
|
||||||
|
DialogButton(
|
||||||
|
text: translations.defaultDialogSecondaryAction,
|
||||||
|
type: DialogButtonType.only,
|
||||||
|
onTap: onStop
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -17,8 +17,8 @@ class FileSelector extends StatefulWidget {
|
|||||||
final bool folder;
|
final bool folder;
|
||||||
final void Function(String)? onSelected;
|
final void Function(String)? onSelected;
|
||||||
|
|
||||||
const FileSelector(
|
const FileSelector({
|
||||||
{required this.placeholder,
|
required this.placeholder,
|
||||||
required this.windowTitle,
|
required this.windowTitle,
|
||||||
required this.controller,
|
required this.controller,
|
||||||
required this.folder,
|
required this.folder,
|
||||||
@@ -29,8 +29,7 @@ class FileSelector extends StatefulWidget {
|
|||||||
this.extension,
|
this.extension,
|
||||||
this.validatorMode,
|
this.validatorMode,
|
||||||
this.onSelected,
|
this.onSelected,
|
||||||
Key? key})
|
Key? key}) : assert(folder || extension != null, "Missing extension for file selector"),
|
||||||
: assert(folder || extension != null, "Missing extension for file selector"),
|
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/message/onboard.dart';
|
import 'package:reboot_launcher/src/message/onboard.dart';
|
||||||
|
|
||||||
@@ -1,110 +1,110 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
InfoBarEntry? onBackendResult(AuthBackendType type, AuthBackendResult event) {
|
SnackBar? onBackendResult(AuthBackendType type, AuthBackendEvent event) {
|
||||||
switch (event.type) {
|
switch (event.type) {
|
||||||
case AuthBackendResultType.starting:
|
case AuthBackendEventType.starting:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.startingServer,
|
translations.startingServer,
|
||||||
severity: InfoBarSeverity.info,
|
severity: InfoBarSeverity.info,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startSuccess:
|
case AuthBackendEventType.startSuccess:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
type == AuthBackendType.local
|
type == AuthBackendType.local
|
||||||
? translations.checkedServer
|
? translations.checkedServer
|
||||||
: translations.startedServer,
|
: translations.startedServer,
|
||||||
severity: InfoBarSeverity.success
|
severity: InfoBarSeverity.success
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startError:
|
case AuthBackendEventType.startError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
type == AuthBackendType.local
|
type == AuthBackendType.local
|
||||||
? translations.localServerError(event.error ?? translations.unknownError)
|
? translations.localServerError(event.error ?? translations.unknownError)
|
||||||
: translations.startServerError(event.error ?? translations.unknownError),
|
: translations.startServerError(event.error ?? translations.unknownError),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.stopping:
|
case AuthBackendEventType.stopping:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.stoppingServer,
|
translations.stoppingServer,
|
||||||
severity: InfoBarSeverity.info,
|
severity: InfoBarSeverity.info,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.stopSuccess:
|
case AuthBackendEventType.stopSuccess:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.stoppedServer,
|
translations.stoppedServer,
|
||||||
severity: InfoBarSeverity.success
|
severity: InfoBarSeverity.success
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.stopError:
|
case AuthBackendEventType.stopError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.stopServerError(event.error ?? translations.unknownError),
|
translations.stopServerError(event.error ?? translations.unknownError),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startMissingHostError:
|
case AuthBackendEventType.startMissingHostError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.missingHostNameError,
|
translations.missingHostNameError,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startMissingPortError:
|
case AuthBackendEventType.startMissingPortError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.missingPortError,
|
translations.missingPortError,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startIllegalPortError:
|
case AuthBackendEventType.startIllegalPortError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.illegalPortError,
|
translations.illegalPortError,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startFreeingPort:
|
case AuthBackendEventType.startFreeingPort:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.freeingPort,
|
translations.freeingPort,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startFreePortSuccess:
|
case AuthBackendEventType.startFreePortSuccess:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.freedPort,
|
translations.freedPort,
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarShortDuration
|
duration: infoBarShortDuration
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startFreePortError:
|
case AuthBackendEventType.startFreePortError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.freePortError(event.error ?? translations.unknownError),
|
translations.freePortError(event.error ?? translations.unknownError),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startPingingRemote:
|
case AuthBackendEventType.startPingingRemote:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.pingingServer(AuthBackendType.remote.name),
|
translations.pingingServer(AuthBackendType.remote.name),
|
||||||
severity: InfoBarSeverity.info,
|
severity: InfoBarSeverity.info,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startPingingLocal:
|
case AuthBackendEventType.startPingingLocal:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.pingingServer(type.name),
|
translations.pingingServer(type.name),
|
||||||
severity: InfoBarSeverity.info,
|
severity: InfoBarSeverity.info,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startPingError:
|
case AuthBackendEventType.startPingError:
|
||||||
return showRebootInfoBar(
|
return SnackBar.open(
|
||||||
translations.pingError(type.name),
|
translations.pingError(type.name),
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
);
|
);
|
||||||
case AuthBackendResultType.startedImplementation:
|
case AuthBackendEventType.startedImplementation:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void onBackendError(Object error) {
|
void onBackendError(Object error) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.backendErrorMessage,
|
translations.backendErrorMessage,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -5,22 +5,22 @@ import 'package:flutter/services.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/keyboard.dart';
|
import 'package:reboot_launcher/src/util/keyboard.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/section/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/message/data.dart';
|
import 'package:reboot_launcher/src/message/data.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/button/backend_start_button.dart';
|
import 'package:reboot_launcher/src/widget/section/backend/state_toggle.dart';
|
||||||
import 'package:reboot_launcher/src/button/server_type_selector.dart';
|
import 'package:reboot_launcher/src/widget/section/backend/type_selector.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
final GlobalKey<OverlayTargetState> backendTypeOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> backendTypeOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> backendGameServerAddressOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> backendGameServerAddressOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> backendUnrealEngineOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> backendUnrealEngineOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> backendDetachedOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> backendDetachedOverlayTargetKey = GlobalKey();
|
||||||
|
|
||||||
class BackendPage extends AbstractPage {
|
class BackendPage extends AbstractPage {
|
||||||
const BackendPage({Key? key}) : super(key: key);
|
const BackendPage({Key? key}) : super(key: key);
|
||||||
@@ -44,12 +44,12 @@ class BackendPage extends AbstractPage {
|
|||||||
class _BackendPageState extends AbstractPageState<BackendPage> {
|
class _BackendPageState extends AbstractPageState<BackendPage> {
|
||||||
final BackendController _backendController = Get.find<BackendController>();
|
final BackendController _backendController = Get.find<BackendController>();
|
||||||
|
|
||||||
InfoBarEntry? _infoBarEntry;
|
SnackBar? _SnackBar;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
ServicesBinding.instance.keyboard.addHandler((keyEvent) {
|
ServicesBinding.instance.keyboard.addHandler((keyEvent) {
|
||||||
if(_infoBarEntry == null) {
|
if(_SnackBar == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +57,8 @@ class _BackendPageState extends AbstractPageState<BackendPage> {
|
|||||||
_backendController.consoleKey.value = keyEvent.physicalKey;
|
_backendController.consoleKey.value = keyEvent.physicalKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
_infoBarEntry?.close();
|
_SnackBar?.close();
|
||||||
_infoBarEntry = null;
|
_SnackBar = null;
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
super.initState();
|
super.initState();
|
||||||
@@ -87,7 +87,7 @@ class _BackendPageState extends AbstractPageState<BackendPage> {
|
|||||||
),
|
),
|
||||||
title: Text(translations.matchmakerConfigurationAddressName),
|
title: Text(translations.matchmakerConfigurationAddressName),
|
||||||
subtitle: Text(translations.matchmakerConfigurationAddressDescription),
|
subtitle: Text(translations.matchmakerConfigurationAddressDescription),
|
||||||
content: OverlayTarget(
|
content: TutorialOverlayTarget(
|
||||||
key: backendGameServerAddressOverlayTargetKey,
|
key: backendGameServerAddressOverlayTargetKey,
|
||||||
child: TextFormBox(
|
child: TextFormBox(
|
||||||
placeholder: translations.matchmakerConfigurationAddressName,
|
placeholder: translations.matchmakerConfigurationAddressName,
|
||||||
@@ -158,7 +158,7 @@ class _BackendPageState extends AbstractPageState<BackendPage> {
|
|||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16.0
|
width: 16.0
|
||||||
),
|
),
|
||||||
OverlayTarget(
|
TutorialOverlayTarget(
|
||||||
key: backendDetachedOverlayTargetKey,
|
key: backendDetachedOverlayTargetKey,
|
||||||
child: ToggleSwitch(
|
child: ToggleSwitch(
|
||||||
checked: _backendController.detached(),
|
checked: _backendController.detached(),
|
||||||
@@ -182,11 +182,11 @@ class _BackendPageState extends AbstractPageState<BackendPage> {
|
|||||||
title: Text(translations.settingsClientConsoleKeyName),
|
title: Text(translations.settingsClientConsoleKeyName),
|
||||||
subtitle: Text(translations.settingsClientConsoleKeyDescription),
|
subtitle: Text(translations.settingsClientConsoleKeyDescription),
|
||||||
contentWidth: null,
|
contentWidth: null,
|
||||||
content: OverlayTarget(
|
content: TutorialOverlayTarget(
|
||||||
key: backendUnrealEngineOverlayTargetKey,
|
key: backendUnrealEngineOverlayTargetKey,
|
||||||
child: Button(
|
child: Button(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_infoBarEntry = showRebootInfoBar(
|
_SnackBar = SnackBar.open(
|
||||||
translations.clickKey,
|
translations.clickKey,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
@@ -234,7 +234,7 @@ class _BackendPageState extends AbstractPageState<BackendPage> {
|
|||||||
),
|
),
|
||||||
title: Text(translations.backendTypeName),
|
title: Text(translations.backendTypeName),
|
||||||
subtitle: Text(translations.backendTypeDescription),
|
subtitle: Text(translations.backendTypeDescription),
|
||||||
content: ServerTypeSelector(
|
content: BackendTypeSelector(
|
||||||
overlayKey: backendTypeOverlayTargetKey
|
overlayKey: backendTypeOverlayTargetKey
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
@@ -6,11 +6,9 @@ import 'package:reboot_common/common.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/message/backend.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/backend/message.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
|
||||||
|
|
||||||
import '../messenger/info_bar.dart';
|
|
||||||
|
|
||||||
class BackendButton extends StatefulWidget {
|
class BackendButton extends StatefulWidget {
|
||||||
const BackendButton({Key? key}) : super(key: key);
|
const BackendButton({Key? key}) : super(key: key);
|
||||||
@@ -50,18 +48,26 @@ class _BackendButtonState extends State<BackendButton> {
|
|||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
child: StreamBuilder(
|
child: StreamBuilder(
|
||||||
stream: _textController.stream,
|
stream: _textController.stream,
|
||||||
builder: (context, snapshot) => Obx(() => Text(_buttonText))
|
builder: (context, snapshot) => Obx(() => Text(_text))
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () => _backendController.toggle(
|
onPressed: () => _backendController.toggle(
|
||||||
eventHandler: (type, event) {
|
eventHandler: _onEvent,
|
||||||
|
errorHandler: _onError
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
SnackBar? _onEvent(AuthBackendType type, AuthBackendEvent event) {
|
||||||
_backendController.started.value = event.type.isStart && !event.type.isError;
|
_backendController.started.value = event.type.isStart && !event.type.isError;
|
||||||
if(event.type == AuthBackendResultType.startedImplementation) {
|
if(event.type == AuthBackendEventType.startedImplementation) {
|
||||||
_backendController.implementation = event.implementation;
|
_backendController.implementation = event.implementation;
|
||||||
}
|
}
|
||||||
return onBackendResult(type, event);
|
return onBackendResult(type, event);
|
||||||
},
|
}
|
||||||
errorHandler: (error) {
|
|
||||||
|
void _onError(String error) {
|
||||||
if(_backendController.started.value) {
|
if(_backendController.started.value) {
|
||||||
_backendController.stop();
|
_backendController.stop();
|
||||||
_gameController.instance.value?.kill();
|
_gameController.instance.value?.kill();
|
||||||
@@ -69,21 +75,15 @@ class _BackendButtonState extends State<BackendButton> {
|
|||||||
onBackendError(error);
|
onBackendError(error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
|
String get _text {
|
||||||
String get _buttonText {
|
if(_backendController.type.value == AuthBackendType.local
|
||||||
if(_backendController.type.value == AuthBackendType.local && _backendController.port.text.trim() == kDefaultBackendPort.toString()){
|
&& _backendController.port.text.trim() == kDefaultBackendPort.toString()){
|
||||||
return translations.checkServer;
|
return translations.checkServer;
|
||||||
}
|
}else if(_backendController.started.value){
|
||||||
|
|
||||||
if(_backendController.started.value){
|
|
||||||
return translations.stopServer;
|
return translations.stopServer;
|
||||||
}
|
}else {
|
||||||
|
|
||||||
return translations.startServer;
|
return translations.startServer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
@@ -3,35 +3,34 @@ import 'package:get/get.dart';
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
|
||||||
|
|
||||||
class ServerTypeSelector extends StatefulWidget {
|
class BackendTypeSelector extends StatefulWidget {
|
||||||
final Key overlayKey;
|
final Key overlayKey;
|
||||||
const ServerTypeSelector({required this.overlayKey});
|
const BackendTypeSelector({required this.overlayKey});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ServerTypeSelector> createState() => _ServerTypeSelectorState();
|
State<BackendTypeSelector> createState() => _BackendTypeSelectorState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ServerTypeSelectorState extends State<ServerTypeSelector> {
|
class _BackendTypeSelectorState extends State<BackendTypeSelector> {
|
||||||
late final BackendController _backendController = Get.find<BackendController>();
|
late final BackendController _backendController = Get.find<BackendController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Obx(() => OverlayTarget(
|
return Obx(() => TutorialOverlayTarget(
|
||||||
key: widget.overlayKey,
|
key: widget.overlayKey,
|
||||||
child: DropDownButton(
|
child: DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(_backendController.type.value.label),
|
leading: Text(_backendController.type.value.label),
|
||||||
items: AuthBackendType.values
|
items: _items
|
||||||
.map((type) => _createItem(type))
|
|
||||||
.toList()
|
|
||||||
),
|
),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<MenuFlyoutItem> get _items => AuthBackendType.values
|
||||||
|
.map((type) => _createItem(type))
|
||||||
|
.toList();
|
||||||
|
|
||||||
MenuFlyoutItem _createItem(AuthBackendType type) => MenuFlyoutItem(
|
MenuFlyoutItem _createItem(AuthBackendType type) => MenuFlyoutItem(
|
||||||
text: Text(type.label),
|
text: Text(type.label),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -42,9 +41,9 @@ class _ServerTypeSelectorState extends State<ServerTypeSelector> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension _ServerTypeExtension on AuthBackendType {
|
extension _ServerTypeExtension on AuthBackendType {
|
||||||
String get label {
|
String get label => switch(this) {
|
||||||
return this == AuthBackendType.embedded ? translations.embedded
|
AuthBackendType.embedded => translations.embedded,
|
||||||
: this == AuthBackendType.remote ? translations.remote
|
AuthBackendType.remote => translations.remote,
|
||||||
: translations.local;
|
AuthBackendType.local => translations.local
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
13
gui/lib/src/widget/page/browser/filter.dart
Normal file
13
gui/lib/src/widget/page/browser/filter.dart
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
|
||||||
|
enum ServerBrowserFilter {
|
||||||
|
all,
|
||||||
|
accessible,
|
||||||
|
playable;
|
||||||
|
|
||||||
|
String get translatedName => switch(this) {
|
||||||
|
ServerBrowserFilter.all => translations.all,
|
||||||
|
ServerBrowserFilter.accessible => translations.accessible,
|
||||||
|
ServerBrowserFilter.playable => translations.playable
|
||||||
|
};
|
||||||
|
}
|
||||||
15
gui/lib/src/widget/page/browser/order.dart
Normal file
15
gui/lib/src/widget/page/browser/order.dart
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
|
||||||
|
enum ServerBrowserOrder {
|
||||||
|
timeAscending,
|
||||||
|
timeDescending,
|
||||||
|
nameAscending,
|
||||||
|
nameDescending;
|
||||||
|
|
||||||
|
String get translatedName => switch(this) {
|
||||||
|
ServerBrowserOrder.timeAscending => translations.timeAscending,
|
||||||
|
ServerBrowserOrder.timeDescending => translations.timeDescending,
|
||||||
|
ServerBrowserOrder.nameAscending => translations.nameAscending,
|
||||||
|
ServerBrowserOrder.nameDescending => translations.nameDescending
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -11,16 +11,20 @@ import 'package:reboot_launcher/src/controller/backend_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/dialog/dialog.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/dialog/dialog_button.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/dialog/form_dialog.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/browser/filter.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/browser/order.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/sections.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/cryptography.dart';
|
import 'package:reboot_launcher/src/util/cryptography.dart';
|
||||||
import 'package:reboot_launcher/src/util/extensions.dart';
|
import 'package:reboot_launcher/src/util/extensions.dart';
|
||||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/section/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
|
||||||
|
|
||||||
class BrowsePage extends AbstractPage {
|
class BrowsePage extends AbstractPage {
|
||||||
const BrowsePage({Key? key}) : super(key: key);
|
const BrowsePage({Key? key}) : super(key: key);
|
||||||
@@ -49,8 +53,8 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
final TextEditingController _filterController = TextEditingController();
|
final TextEditingController _filterController = TextEditingController();
|
||||||
final StreamController<String> _filterControllerStream = StreamController.broadcast();
|
final StreamController<String> _filterControllerStream = StreamController.broadcast();
|
||||||
|
|
||||||
final Rx<_Filter> _filter = Rx(_Filter.all);
|
final Rx<ServerBrowserFilter> _filter = Rx(ServerBrowserFilter.all);
|
||||||
final Rx<_Sort> _sort = Rx(_Sort.timeDescending);
|
final Rx<ServerBrowserOrder> _sort = Rx(ServerBrowserOrder.timeDescending);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -76,7 +80,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
if(server != null) {
|
if(server != null) {
|
||||||
_joinServer(_hostingController.uuid, server);
|
_joinServer(_hostingController.uuid, server);
|
||||||
}else {
|
}else {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.noServerFound,
|
translations.noServerFound,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -124,11 +128,11 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
),
|
),
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
_buildFilter(context),
|
_buildFilterSection(context),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
width: 16.0
|
width: 16.0
|
||||||
),
|
),
|
||||||
_buildSort(context),
|
_buildOrderSection(context),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(
|
const SizedBox(
|
||||||
@@ -142,7 +146,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildSort(BuildContext context) => Row(
|
Widget _buildOrderSection(BuildContext context) => Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
fluentIcons.FluentIcons.arrow_sort_24_regular,
|
fluentIcons.FluentIcons.arrow_sort_24_regular,
|
||||||
@@ -159,24 +163,27 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
Obx(() => SizedBox(
|
Obx(() => SizedBox(
|
||||||
width: 230,
|
width: 230,
|
||||||
child: DropDownButton(
|
child: DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(
|
leading: Text(
|
||||||
_sort.value.translatedName,
|
_sort.value.translatedName,
|
||||||
textAlign: TextAlign.start
|
textAlign: TextAlign.start
|
||||||
),
|
),
|
||||||
title: const Spacer(),
|
title: const Spacer(),
|
||||||
items: _Sort.values.map((entry) => MenuFlyoutItem(
|
items: _orders
|
||||||
text: Text(entry.translatedName),
|
|
||||||
onPressed: () => _sort.value = entry
|
|
||||||
)).toList()
|
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
Row _buildFilter(BuildContext context) {
|
List<MenuFlyoutItem> get _orders => ServerBrowserOrder.values
|
||||||
return Row(
|
.map(_buildOrder)
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
MenuFlyoutItem _buildOrder(ServerBrowserOrder entry) => MenuFlyoutItem(
|
||||||
|
text: Text(entry.translatedName),
|
||||||
|
onPressed: () => _sort.value = entry
|
||||||
|
);
|
||||||
|
|
||||||
|
Widget _buildFilterSection(BuildContext context) => Row(
|
||||||
children: [
|
children: [
|
||||||
Icon(
|
Icon(
|
||||||
fluentIcons.FluentIcons.filter_24_regular,
|
fluentIcons.FluentIcons.filter_24_regular,
|
||||||
@@ -193,45 +200,48 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
Obx(() => SizedBox(
|
Obx(() => SizedBox(
|
||||||
width: 125,
|
width: 125,
|
||||||
child: DropDownButton(
|
child: DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(
|
leading: Text(
|
||||||
_filter.value.translatedName,
|
_filter.value.translatedName,
|
||||||
textAlign: TextAlign.start
|
textAlign: TextAlign.start
|
||||||
),
|
),
|
||||||
title: const Spacer(),
|
title: const Spacer(),
|
||||||
items: _Filter.values.map((entry) => MenuFlyoutItem(
|
items: _filters
|
||||||
text: Text(entry.translatedName),
|
|
||||||
onPressed: () => _filter.value = entry
|
|
||||||
)).toList()
|
|
||||||
),
|
),
|
||||||
))
|
))
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
List<MenuFlyoutItem> get _filters => ServerBrowserFilter.values
|
||||||
|
.map((entry) => _buildFilter(entry))
|
||||||
|
.toList();
|
||||||
|
|
||||||
|
MenuFlyoutItem _buildFilter(ServerBrowserFilter entry) => MenuFlyoutItem(
|
||||||
|
text: Text(entry.translatedName),
|
||||||
|
onPressed: () => _filter.value = entry
|
||||||
|
);
|
||||||
|
|
||||||
Widget _buildPopulatedListBody(List<ServerBrowserEntry>? items) => Obx(() {
|
Widget _buildPopulatedListBody(List<ServerBrowserEntry>? items) => Obx(() {
|
||||||
final filter = _filter.value;
|
final filter = _filter.value;
|
||||||
final sorted = items?.where((element) {
|
final sorted = items?.where((element) {
|
||||||
switch(filter) {
|
switch(filter) {
|
||||||
case _Filter.all:
|
case ServerBrowserFilter.all:
|
||||||
return true;
|
return true;
|
||||||
case _Filter.accessible:
|
case ServerBrowserFilter.accessible:
|
||||||
return element.password.isNotEmpty;
|
return element.password.isNotEmpty;
|
||||||
case _Filter.playable:
|
case ServerBrowserFilter.playable:
|
||||||
return _gameController.getVersionByGame(element.version) != null;
|
return _gameController.getVersionByGame(element.version) != null;
|
||||||
}
|
}
|
||||||
}).toList();
|
}).toList();
|
||||||
final sort = _sort.value;
|
final sort = _sort.value;
|
||||||
sorted?.sort((first, second) {
|
sorted?.sort((first, second) {
|
||||||
switch(sort) {
|
switch(sort) {
|
||||||
case _Sort.timeAscending:
|
case ServerBrowserOrder.timeAscending:
|
||||||
return first.timestamp.compareTo(second.timestamp);
|
return first.timestamp.compareTo(second.timestamp);
|
||||||
case _Sort.timeDescending:
|
case ServerBrowserOrder.timeDescending:
|
||||||
return second.timestamp.compareTo(first.timestamp);
|
return second.timestamp.compareTo(first.timestamp);
|
||||||
case _Sort.nameAscending:
|
case ServerBrowserOrder.nameAscending:
|
||||||
return first.name.compareTo(second.name);
|
return first.name.compareTo(second.name);
|
||||||
case _Sort.nameDescending:
|
case ServerBrowserOrder.nameDescending:
|
||||||
return second.name.compareTo(first.name);
|
return second.name.compareTo(first.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -369,7 +379,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
|
|
||||||
Future<void> _joinServer(String uuid, ServerBrowserEntry server) async {
|
Future<void> _joinServer(String uuid, ServerBrowserEntry server) async {
|
||||||
if(!kDebugMode && uuid == server.id) {
|
if(!kDebugMode && uuid == server.id) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.joinSelfServer,
|
translations.joinSelfServer,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -379,7 +389,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
|
|
||||||
final version = _gameController.getVersionByGame(server.version.toString());
|
final version = _gameController.getVersionByGame(server.version.toString());
|
||||||
if(version == null) {
|
if(version == null) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.cannotJoinServerVersion(server.version.toString()),
|
translations.cannotJoinServerVersion(server.version.toString()),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -407,7 +417,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!checkPassword(confirmPassword, hashedPassword)) {
|
if(!checkPassword(confirmPassword, hashedPassword)) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.wrongServerPassword,
|
translations.wrongServerPassword,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -425,7 +435,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<bool> _isServerValid(String name, String address) async {
|
Future<bool> _isServerValid(String name, String address) async {
|
||||||
final loadingBar = showRebootInfoBar(
|
final loadingBar = SnackBar.open(
|
||||||
translations.joiningServer(name),
|
translations.joiningServer(name),
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
loading: true,
|
loading: true,
|
||||||
@@ -438,7 +448,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.offlineServer,
|
translations.offlineServer,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.error
|
severity: InfoBarSeverity.error
|
||||||
@@ -450,7 +460,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
final confirmPasswordController = TextEditingController();
|
final confirmPasswordController = TextEditingController();
|
||||||
final showPassword = RxBool(false);
|
final showPassword = RxBool(false);
|
||||||
final showPasswordTrailing = RxBool(false);
|
final showPasswordTrailing = RxBool(false);
|
||||||
return await showRebootDialog<String?>(
|
return await Dialog.open<String?>(
|
||||||
builder: (context) => FormDialog(
|
builder: (context) => FormDialog(
|
||||||
content: Column(
|
content: Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -486,12 +496,12 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
buttons: [
|
buttons: [
|
||||||
DialogButton(
|
DialogButton(
|
||||||
text: translations.serverPasswordCancel,
|
text: translations.serverPasswordCancel,
|
||||||
type: ButtonType.secondary
|
type: DialogButtonType.secondary
|
||||||
),
|
),
|
||||||
|
|
||||||
DialogButton(
|
DialogButton(
|
||||||
text: translations.serverPasswordConfirm,
|
text: translations.serverPasswordConfirm,
|
||||||
type: ButtonType.primary,
|
type: DialogButtonType.primary,
|
||||||
onTap: () => Navigator.of(context).pop(confirmPasswordController.text)
|
onTap: () => Navigator.of(context).pop(confirmPasswordController.text)
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
@@ -507,7 +517,7 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
FlutterClipboard.controlC(decryptedIp);
|
FlutterClipboard.controlC(decryptedIp);
|
||||||
}
|
}
|
||||||
Get.find<GameController>().selectedVersion.value = version;
|
Get.find<GameController>().selectedVersion.value = version;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => showRebootInfoBar(
|
WidgetsBinding.instance.addPostFrameCallback((_) => SnackBar.open(
|
||||||
embedded ? translations.joinedServer(author) : translations.copiedIp,
|
embedded ? translations.joinedServer(author) : translations.copiedIp,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
severity: InfoBarSeverity.success
|
severity: InfoBarSeverity.success
|
||||||
@@ -520,40 +530,3 @@ class _BrowsePageState extends AbstractPageState<BrowsePage> {
|
|||||||
@override
|
@override
|
||||||
List<Widget> get settings => [];
|
List<Widget> get settings => [];
|
||||||
}
|
}
|
||||||
|
|
||||||
enum _Filter {
|
|
||||||
all,
|
|
||||||
accessible,
|
|
||||||
playable;
|
|
||||||
|
|
||||||
String get translatedName {
|
|
||||||
switch(this) {
|
|
||||||
case _Filter.all:
|
|
||||||
return translations.all;
|
|
||||||
case _Filter.accessible:
|
|
||||||
return translations.accessible;
|
|
||||||
case _Filter.playable:
|
|
||||||
return translations.playable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum _Sort {
|
|
||||||
timeAscending,
|
|
||||||
timeDescending,
|
|
||||||
nameAscending,
|
|
||||||
nameDescending;
|
|
||||||
|
|
||||||
String get translatedName {
|
|
||||||
switch(this) {
|
|
||||||
case _Sort.timeAscending:
|
|
||||||
return translations.timeAscending;
|
|
||||||
case _Sort.timeDescending:
|
|
||||||
return translations.timeDescending;
|
|
||||||
case _Sort.nameAscending:
|
|
||||||
return translations.nameAscending;
|
|
||||||
case _Sort.nameDescending:
|
|
||||||
return translations.nameDescending;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,22 +9,22 @@ import 'package:reboot_launcher/main.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/message/data.dart';
|
import 'package:reboot_launcher/src/message/data.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/button/game_start_button.dart';
|
import 'package:reboot_launcher/src/widget/section/play/state_toggle.dart';
|
||||||
import 'package:reboot_launcher/src/button/version_selector.dart';
|
import 'package:reboot_launcher/src/widget/version_selector.dart';
|
||||||
|
|
||||||
final GlobalKey<OverlayTargetState> hostVersionOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostVersionOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> hostInfoOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostInfoOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> hostInfoNameOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostInfoNameOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> hostInfoDescriptionOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostInfoDescriptionOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> hostInfoPasswordOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostInfoPasswordOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<OverlayTargetState> hostShareOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> hostShareOverlayTargetKey = GlobalKey();
|
||||||
final GlobalKey<SettingTileState> hostInfoTileKey = GlobalKey();
|
final GlobalKey<SettingTileState> hostInfoTileKey = GlobalKey();
|
||||||
|
|
||||||
class HostPage extends AbstractPage {
|
class HostPage extends AbstractPage {
|
||||||
@@ -100,7 +100,7 @@ class _HostingPageState extends AbstractPageState<HostPage> {
|
|||||||
),
|
),
|
||||||
title: Text(translations.hostGameServerNameName),
|
title: Text(translations.hostGameServerNameName),
|
||||||
subtitle: Text(translations.hostGameServerNameDescription),
|
subtitle: Text(translations.hostGameServerNameDescription),
|
||||||
content: OverlayTarget(
|
content: TutorialOverlayTarget(
|
||||||
key: hostInfoNameOverlayTargetKey,
|
key: hostInfoNameOverlayTargetKey,
|
||||||
child: TextFormBox(
|
child: TextFormBox(
|
||||||
placeholder: translations.hostGameServerNameName,
|
placeholder: translations.hostGameServerNameName,
|
||||||
@@ -116,7 +116,7 @@ class _HostingPageState extends AbstractPageState<HostPage> {
|
|||||||
),
|
),
|
||||||
title: Text(translations.hostGameServerDescriptionName),
|
title: Text(translations.hostGameServerDescriptionName),
|
||||||
subtitle: Text(translations.hostGameServerDescriptionDescription),
|
subtitle: Text(translations.hostGameServerDescriptionDescription),
|
||||||
content: OverlayTarget(
|
content: TutorialOverlayTarget(
|
||||||
key: hostInfoDescriptionOverlayTargetKey,
|
key: hostInfoDescriptionOverlayTargetKey,
|
||||||
child: TextFormBox(
|
child: TextFormBox(
|
||||||
placeholder: translations.hostGameServerDescriptionName,
|
placeholder: translations.hostGameServerDescriptionName,
|
||||||
@@ -132,7 +132,7 @@ class _HostingPageState extends AbstractPageState<HostPage> {
|
|||||||
),
|
),
|
||||||
title: Text(translations.hostGameServerPasswordName),
|
title: Text(translations.hostGameServerPasswordName),
|
||||||
subtitle: Text(translations.hostGameServerPasswordDescription),
|
subtitle: Text(translations.hostGameServerPasswordDescription),
|
||||||
content: Obx(() => OverlayTarget(
|
content: Obx(() => TutorialOverlayTarget(
|
||||||
key: hostInfoPasswordOverlayTargetKey,
|
key: hostInfoPasswordOverlayTargetKey,
|
||||||
child: TextFormBox(
|
child: TextFormBox(
|
||||||
placeholder: translations.hostGameServerPasswordName,
|
placeholder: translations.hostGameServerPasswordName,
|
||||||
@@ -275,7 +275,7 @@ class _HostingPageState extends AbstractPageState<HostPage> {
|
|||||||
subtitle: Text(translations.hostShareIpDescription),
|
subtitle: Text(translations.hostShareIpDescription),
|
||||||
content: Button(
|
content: Button(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
InfoBarEntry? entry;
|
SnackBar? entry;
|
||||||
try {
|
try {
|
||||||
entry = _showCopyingIp();
|
entry = _showCopyingIp();
|
||||||
final ip = await Ipify.ipv4();
|
final ip = await Ipify.ipv4();
|
||||||
@@ -317,29 +317,29 @@ class _HostingPageState extends AbstractPageState<HostPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _showCopiedLink() => showRebootInfoBar(
|
void _showCopiedLink() => SnackBar.open(
|
||||||
translations.hostShareLinkMessageSuccess,
|
translations.hostShareLinkMessageSuccess,
|
||||||
severity: InfoBarSeverity.success
|
severity: InfoBarSeverity.success
|
||||||
);
|
);
|
||||||
|
|
||||||
InfoBarEntry _showCopyingIp() => showRebootInfoBar(
|
SnackBar _showCopyingIp() => SnackBar.open(
|
||||||
translations.hostShareIpMessageLoading,
|
translations.hostShareIpMessageLoading,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
|
|
||||||
void _showCopiedIp() => showRebootInfoBar(
|
void _showCopiedIp() => SnackBar.open(
|
||||||
translations.hostShareIpMessageSuccess,
|
translations.hostShareIpMessageSuccess,
|
||||||
severity: InfoBarSeverity.success
|
severity: InfoBarSeverity.success
|
||||||
);
|
);
|
||||||
|
|
||||||
void _showCannotCopyIp(Object error) => showRebootInfoBar(
|
void _showCannotCopyIp(Object error) => SnackBar.open(
|
||||||
translations.hostShareIpMessageError(error.toString()),
|
translations.hostShareIpMessageError(error.toString()),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
);
|
);
|
||||||
|
|
||||||
void _showCannotUpdateGameServer(Object error) => showRebootInfoBar(
|
void _showCannotUpdateGameServer(Object error) => SnackBar.open(
|
||||||
translations.cannotUpdateGameServer(error.toString()),
|
translations.cannotUpdateGameServer(error.toString()),
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/section/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/message/onboard.dart';
|
import 'package:reboot_launcher/src/message/onboard.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
|
|
||||||
class InfoPage extends AbstractPage {
|
class InfoPage extends AbstractPage {
|
||||||
@@ -13,22 +13,21 @@ import 'package:reboot_launcher/src/controller/game_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_suggestion.dart';
|
import 'package:reboot_launcher/src/widget/page/page_suggestion.dart';
|
||||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||||
import 'package:reboot_launcher/src/util/os.dart';
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/profile_tile.dart';
|
import 'package:reboot_launcher/src/widget/profile_tile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
|
||||||
import 'package:reboot_launcher/src/util/updater.dart';
|
import 'package:reboot_launcher/src/util/updater.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar_area.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar_area.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:window_manager/window_manager.dart';
|
import 'package:window_manager/window_manager.dart';
|
||||||
|
|
||||||
final GlobalKey<OverlayTargetState> profileOverlayKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> profileOverlayKey = GlobalKey();
|
||||||
const double _kDefaultPadding = 12.0;
|
const double _kDefaultPadding = 12.0;
|
||||||
|
|
||||||
class RebootPager extends StatefulWidget {
|
class RebootPager extends StatefulWidget {
|
||||||
@@ -92,7 +91,7 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
}
|
}
|
||||||
|
|
||||||
_backendController.gameServerAddress.text = kDefaultGameServerHost;
|
_backendController.gameServerAddress.text = kDefaultGameServerHost;
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) => showRebootInfoBar(
|
WidgetsBinding.instance.addPostFrameCallback((_) => SnackBar.open(
|
||||||
translations.serverNoLongerAvailableUnnamed,
|
translations.serverNoLongerAvailableUnnamed,
|
||||||
severity: InfoBarSeverity.warning,
|
severity: InfoBarSeverity.warning,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
@@ -106,8 +105,8 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
void _checkUpdates() {
|
void _checkUpdates() {
|
||||||
checkLauncherUpdate(
|
checkLauncherUpdate(
|
||||||
onUpdate: (latestVersion) {
|
onUpdate: (latestVersion) {
|
||||||
late InfoBarEntry infoBar;
|
late SnackBar infoBar;
|
||||||
infoBar = showRebootInfoBar(
|
infoBar = SnackBar.open(
|
||||||
translations.updateAvailable(latestVersion.toString()),
|
translations.updateAvailable(latestVersion.toString()),
|
||||||
duration: null,
|
duration: null,
|
||||||
severity: InfoBarSeverity.warning,
|
severity: InfoBarSeverity.warning,
|
||||||
@@ -312,7 +311,7 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
fit: StackFit.loose,
|
fit: StackFit.loose,
|
||||||
children: [
|
children: [
|
||||||
_buildBodyContent(),
|
_buildBodyContent(),
|
||||||
InfoBarArea(
|
SnackBarArea(
|
||||||
key: infoBarAreaKey
|
key: infoBarAreaKey
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -414,10 +413,6 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
return TextSpan(
|
return TextSpan(
|
||||||
text: name,
|
text: name,
|
||||||
recognizer: last ? null : (TapGestureRecognizer()..onTap = () {
|
recognizer: last ? null : (TapGestureRecognizer()..onTap = () {
|
||||||
if(inDialog) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pops = length - 1 - index;
|
var pops = length - 1 - index;
|
||||||
while(pops-- > 0) {
|
while(pops-- > 0) {
|
||||||
Navigator.of(pageKey.currentContext!).pop();
|
Navigator.of(pageKey.currentContext!).pop();
|
||||||
@@ -439,7 +434,7 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
children: [
|
children: [
|
||||||
Obx(() {
|
Obx(() {
|
||||||
pageIndex.value;
|
pageIndex.value;
|
||||||
return ProfileWidget(
|
return ProfileTile(
|
||||||
overlayKey: profileOverlayKey
|
overlayKey: profileOverlayKey
|
||||||
);
|
);
|
||||||
}),
|
}),
|
||||||
@@ -470,7 +465,7 @@ class _RebootPagerState extends State<RebootPager> with WindowListener, Automati
|
|||||||
|
|
||||||
Widget _buildNavigationItem(AbstractPage page) {
|
Widget _buildNavigationItem(AbstractPage page) {
|
||||||
final index = page.type.index;
|
final index = page.type.index;
|
||||||
return OverlayTarget(
|
return TutorialOverlayTarget(
|
||||||
key: getOverlayTargetKeyByPage(index),
|
key: getOverlayTargetKeyByPage(index),
|
||||||
child: HoverButton(
|
child: HoverButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
@@ -3,16 +3,16 @@ import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/section/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/message/data.dart';
|
import 'package:reboot_launcher/src/message/data.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/button/game_start_button.dart';
|
import 'package:reboot_launcher/src/widget/section/play/state_toggle.dart';
|
||||||
import 'package:reboot_launcher/src/button/version_selector.dart';
|
import 'package:reboot_launcher/src/widget/section/version_selector.dart';
|
||||||
|
|
||||||
final GlobalKey<OverlayTargetState> gameVersionOverlayTargetKey = GlobalKey();
|
final GlobalKey<TutorialOverlayTargetState> gameVersionOverlayTargetKey = GlobalKey();
|
||||||
|
|
||||||
class PlayPage extends AbstractPage {
|
class PlayPage extends AbstractPage {
|
||||||
const PlayPage({Key? key}) : super(key: key);
|
const PlayPage({Key? key}) : super(key: key);
|
||||||
@@ -14,11 +14,13 @@ import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
|||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
import 'package:reboot_launcher/src/controller/server_browser_controller.dart';
|
||||||
import 'package:reboot_launcher/src/message/backend.dart';
|
import 'package:reboot_launcher/src/widget/dialog/dialog.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/dialog/dialog_button.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/dialog/info_dialog.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/backend/message.dart';
|
||||||
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
import 'package:reboot_launcher/src/util/matchmaker.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
import 'package:url_launcher/url_launcher_string.dart';
|
import 'package:url_launcher/url_launcher_string.dart';
|
||||||
import 'package:version/version.dart';
|
import 'package:version/version.dart';
|
||||||
@@ -43,8 +45,8 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
final DllController _dllController = Get.find<DllController>();
|
final DllController _dllController = Get.find<DllController>();
|
||||||
final ServerBrowserController _serverBrowserController = Get.find<ServerBrowserController>();
|
final ServerBrowserController _serverBrowserController = Get.find<ServerBrowserController>();
|
||||||
|
|
||||||
InfoBarEntry? _gameClientInfoBar;
|
SnackBar? _gameClientInfoBar;
|
||||||
InfoBarEntry? _gameServerInfoBar;
|
SnackBar? _gameServerInfoBar;
|
||||||
CancelableOperation? _operation;
|
CancelableOperation? _operation;
|
||||||
Completer? _pingOperation;
|
Completer? _pingOperation;
|
||||||
|
|
||||||
@@ -106,7 +108,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
final backendResult = _backendController.started() || await _backendController.toggle(
|
final backendResult = _backendController.started() || await _backendController.toggle(
|
||||||
eventHandler: (type, event) {
|
eventHandler: (type, event) {
|
||||||
_backendController.started.value = event.type.isStart && !event.type.isError;
|
_backendController.started.value = event.type.isStart && !event.type.isError;
|
||||||
if(event.type == AuthBackendResultType.startedImplementation) {
|
if(event.type == AuthBackendEventType.startedImplementation) {
|
||||||
_backendController.implementation = event.implementation;
|
_backendController.implementation = event.implementation;
|
||||||
}
|
}
|
||||||
return onBackendResult(type, event);
|
return onBackendResult(type, event);
|
||||||
@@ -211,16 +213,16 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final result = await showRebootDialog<bool>(
|
final result = await Dialog.open<bool>(
|
||||||
builder: (context) => InfoDialog(
|
builder: (context) => InfoDialog(
|
||||||
text: translations.automaticGameServerDialogContent,
|
text: translations.automaticGameServerDialogContent,
|
||||||
buttons: [
|
buttons: [
|
||||||
DialogButton(
|
DialogButton(
|
||||||
type: ButtonType.secondary,
|
type: DialogButtonType.secondary,
|
||||||
text: translations.automaticGameServerDialogIgnore
|
text: translations.automaticGameServerDialogIgnore
|
||||||
),
|
),
|
||||||
DialogButton(
|
DialogButton(
|
||||||
type: ButtonType.primary,
|
type: DialogButtonType.primary,
|
||||||
text: translations.automaticGameServerDialogStart,
|
text: translations.automaticGameServerDialogStart,
|
||||||
onTap: () => Navigator.of(context).pop(true),
|
onTap: () => Navigator.of(context).pop(true),
|
||||||
),
|
),
|
||||||
@@ -469,7 +471,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
void _onGameClientInjected() {
|
void _onGameClientInjected() {
|
||||||
_gameClientInfoBar?.close();
|
_gameClientInfoBar?.close();
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.gameClientStarted,
|
translations.gameClientStarted,
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
@@ -488,7 +490,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
final started = await _checkLocalGameServer(gameServerPort);
|
final started = await _checkLocalGameServer(gameServerPort);
|
||||||
if(!started) {
|
if(!started) {
|
||||||
if (_hostingController.instance.value?.killed != true) {
|
if (_hostingController.instance.value?.killed != true) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.gameServerStartWarning,
|
translations.gameServerStartWarning,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
@@ -500,7 +502,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
final accessible = await _checkPublicGameServer(gameServerPort);
|
final accessible = await _checkPublicGameServer(gameServerPort);
|
||||||
if (!accessible) {
|
if (!accessible) {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.gameServerStartLocalWarning,
|
translations.gameServerStartLocalWarning,
|
||||||
severity: InfoBarSeverity.warning,
|
severity: InfoBarSeverity.warning,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -514,7 +516,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
final serverBrowserEntry = await _hostingController.createServerBrowserEntry();
|
final serverBrowserEntry = await _hostingController.createServerBrowserEntry();
|
||||||
await _serverBrowserController.addServer(serverBrowserEntry);
|
await _serverBrowserController.addServer(serverBrowserEntry);
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.gameServerStarted,
|
translations.gameServerStarted,
|
||||||
severity: InfoBarSeverity.success,
|
severity: InfoBarSeverity.success,
|
||||||
duration: infoBarLongDuration
|
duration: infoBarLongDuration
|
||||||
@@ -526,7 +528,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
Future<bool> _checkLocalGameServer(String gameServerPort) async {
|
Future<bool> _checkLocalGameServer(String gameServerPort) async {
|
||||||
try {
|
try {
|
||||||
_gameServerInfoBar = showRebootInfoBar(
|
_gameServerInfoBar = SnackBar.open(
|
||||||
translations.waitingForGameServer,
|
translations.waitingForGameServer,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
@@ -549,7 +551,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
|
|
||||||
Future<bool> _checkPublicGameServer(String gameServerPort) async {
|
Future<bool> _checkPublicGameServer(String gameServerPort) async {
|
||||||
try {
|
try {
|
||||||
_gameServerInfoBar = showRebootInfoBar(
|
_gameServerInfoBar = SnackBar.open(
|
||||||
translations.checkingGameServer,
|
translations.checkingGameServer,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
@@ -668,21 +670,21 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
case _StopReason.normal:
|
case _StopReason.normal:
|
||||||
break;
|
break;
|
||||||
case _StopReason.missingVersionError:
|
case _StopReason.missingVersionError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.missingVersionError,
|
translations.missingVersionError,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.missingExecutableError:
|
case _StopReason.missingExecutableError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.missingExecutableError,
|
translations.missingExecutableError,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.multipleExecutablesError:
|
case _StopReason.multipleExecutablesError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.multipleExecutablesError(error ?? translations.unknown),
|
translations.multipleExecutablesError(error ?? translations.unknown),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -691,7 +693,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
case _StopReason.exitCode:
|
case _StopReason.exitCode:
|
||||||
if(instance != null && !instance.launched) {
|
if(instance != null && !instance.launched) {
|
||||||
final injectedDlls = instance.injectedDlls;
|
final injectedDlls = instance.injectedDlls;
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.corruptedVersionError(injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
translations.corruptedVersionError(injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -700,7 +702,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
break;
|
break;
|
||||||
case _StopReason.corruptedVersionError:
|
case _StopReason.corruptedVersionError:
|
||||||
final injectedDlls = instance?.injectedDlls ?? [];
|
final injectedDlls = instance?.injectedDlls ?? [];
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.corruptedVersionError(injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
translations.corruptedVersionError(injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -711,14 +713,14 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.corruptedDllError:
|
case _StopReason.corruptedDllError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.corruptedDllError(error ?? translations.unknownError),
|
translations.corruptedDllError(error ?? translations.unknownError),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.missingCustomDllError:
|
case _StopReason.missingCustomDllError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.missingCustomDllError(error!),
|
translations.missingCustomDllError(error!),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -727,7 +729,7 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
case _StopReason.tokenError:
|
case _StopReason.tokenError:
|
||||||
_backendController.stop();
|
_backendController.stop();
|
||||||
final injectedDlls = instance?.injectedDlls;
|
final injectedDlls = instance?.injectedDlls;
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.tokenError(injectedDlls == null || injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
translations.tokenError(injectedDlls == null || injectedDlls.isEmpty ? translations.none : injectedDlls.map((element) => element.name).join(", ")),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -738,21 +740,21 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.crash:
|
case _StopReason.crash:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.fortniteCrashError(host ? translations.gameServer : translations.client),
|
translations.fortniteCrashError(host ? translations.gameServer : translations.client),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.unknownError:
|
case _StopReason.unknownError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.unknownFortniteError(error ?? translations.unknownError),
|
translations.unknownFortniteError(error ?? translations.unknownError),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case _StopReason.gameServerPortError:
|
case _StopReason.gameServerPortError:
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.gameServerPortEqualsBackendPort(kDefaultBackendPort),
|
translations.gameServerPortEqualsBackendPort(kDefaultBackendPort),
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
@@ -829,14 +831,14 @@ class _LaunchButtonState extends State<LaunchButton> {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
InfoBarEntry _showLaunchingGameServerWidget() => _gameServerInfoBar = showRebootInfoBar(
|
SnackBar _showLaunchingGameServerWidget() => _gameServerInfoBar = SnackBar.open(
|
||||||
translations.launchingGameServer,
|
translations.launchingGameServer,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null
|
duration: null
|
||||||
);
|
);
|
||||||
|
|
||||||
InfoBarEntry _showLaunchingGameClientWidget(GameVersion version, bool headless, bool linkedHosting) {
|
SnackBar _showLaunchingGameClientWidget(GameVersion version, bool headless, bool linkedHosting) {
|
||||||
return _gameClientInfoBar = showRebootInfoBar(
|
return _gameClientInfoBar = SnackBar.open(
|
||||||
linkedHosting ? translations.launchingGameClientAndServer : translations.launchingGameClientOnly,
|
linkedHosting ? translations.launchingGameClientAndServer : translations.launchingGameClientOnly,
|
||||||
loading: true,
|
loading: true,
|
||||||
duration: null,
|
duration: null,
|
||||||
142
gui/lib/src/widget/page/settings/file_tile.dart
Normal file
142
gui/lib/src/widget/page/settings/file_tile.dart
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart' as fluentIcons show FluentIcons;
|
||||||
|
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||||
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/os.dart';
|
||||||
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/file_selector.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/section/setting_tile.dart';
|
||||||
|
|
||||||
|
class FileSetting extends StatefulWidget {
|
||||||
|
final GlobalKey<TextFormBoxState> validatorKey;
|
||||||
|
final String title;
|
||||||
|
final String description;
|
||||||
|
final TextEditingController controller;
|
||||||
|
final VoidCallback onReset;
|
||||||
|
final String extension;
|
||||||
|
final bool folder;
|
||||||
|
|
||||||
|
const FileSetting({
|
||||||
|
Key? key,
|
||||||
|
required this.validatorKey,
|
||||||
|
required this.title,
|
||||||
|
required this.description,
|
||||||
|
required this.controller,
|
||||||
|
required this.onReset,
|
||||||
|
this.extension = 'dll',
|
||||||
|
this.folder = false,
|
||||||
|
}) : super(key: key);
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FileSetting> createState() => _FileSettingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FileSettingState extends State<FileSetting> {
|
||||||
|
static const double _kButtonDimensions = 30;
|
||||||
|
static const double _kButtonSpacing = 8;
|
||||||
|
|
||||||
|
String? _validationMessage;
|
||||||
|
bool _selecting = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return SettingTile(
|
||||||
|
icon: const Icon(FluentIcons.document_24_regular),
|
||||||
|
title: Text(widget.title),
|
||||||
|
subtitle: Text(widget.description),
|
||||||
|
contentWidth: SettingTile.kDefaultContentWidth + _kButtonDimensions,
|
||||||
|
content: Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: FileSelector(
|
||||||
|
placeholder: translations.selectPathPlaceholder,
|
||||||
|
windowTitle: translations.selectPathWindowTitle,
|
||||||
|
controller: widget.controller,
|
||||||
|
validator: (text) {
|
||||||
|
final result = _checkDll(text);
|
||||||
|
setState(() => _validationMessage = result);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
extension: widget.extension,
|
||||||
|
folder: widget.folder,
|
||||||
|
validatorMode: AutovalidateMode.always,
|
||||||
|
allowNavigator: false,
|
||||||
|
validatorKey: widget.validatorKey,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: _kButtonSpacing),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: _validationMessage == null ? 0.0 : 20.0),
|
||||||
|
child: Tooltip(
|
||||||
|
message: translations.selectFile,
|
||||||
|
child: Button(
|
||||||
|
style: ButtonStyle(padding: WidgetStateProperty.all(EdgeInsets.zero)),
|
||||||
|
onPressed: _selecting ? null : _onSelectPressed,
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: _kButtonDimensions,
|
||||||
|
child: const Icon(fluentIcons.FluentIcons.open_folder_horizontal),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: _kButtonSpacing),
|
||||||
|
Padding(
|
||||||
|
padding: EdgeInsets.only(bottom: _validationMessage == null ? 0.0 : 20.0),
|
||||||
|
child: Tooltip(
|
||||||
|
message: translations.reset,
|
||||||
|
child: Button(
|
||||||
|
style: ButtonStyle(padding: WidgetStateProperty.all(EdgeInsets.zero)),
|
||||||
|
onPressed: widget.onReset,
|
||||||
|
child: SizedBox.square(
|
||||||
|
dimension: _kButtonDimensions,
|
||||||
|
child: const Icon(FluentIcons.arrow_reset_24_regular),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _onSelectPressed() async {
|
||||||
|
if (_selecting) return;
|
||||||
|
setState(() => _selecting = true);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final picked = await compute(openFilePicker, widget.extension);
|
||||||
|
_updateText(widget.controller, picked);
|
||||||
|
} finally {
|
||||||
|
if (mounted) {
|
||||||
|
setState(() => _selecting = false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateText(TextEditingController controller, String? value) {
|
||||||
|
final text = value ?? controller.text;
|
||||||
|
controller.text = text;
|
||||||
|
controller.selection = TextSelection.collapsed(offset: text.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
String? _checkDll(String? text) {
|
||||||
|
if (text == null || text.isEmpty) {
|
||||||
|
return translations.invalidDllPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
final file = File(text);
|
||||||
|
try {
|
||||||
|
file.readAsBytesSync();
|
||||||
|
} catch (_) {
|
||||||
|
return translations.dllDoesNotExist;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!text.endsWith('.dll')) {
|
||||||
|
return translations.invalidDllExtension;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,12 +8,11 @@ import 'package:reboot_common/common.dart';
|
|||||||
import 'package:reboot_launcher/l10n/reboot_localizations.dart';
|
import 'package:reboot_launcher/l10n/reboot_localizations.dart';
|
||||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/file_setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/section/settings/file_tile.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
|
|
||||||
final GlobalKey<TextFormBoxState> settingsConsoleDllInputKey = GlobalKey();
|
final GlobalKey<TextFormBoxState> settingsConsoleDllInputKey = GlobalKey();
|
||||||
@@ -63,8 +62,8 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
title: Text(translations.settingsClientName),
|
title: Text(translations.settingsClientName),
|
||||||
subtitle: Text(translations.settingsClientDescription),
|
subtitle: Text(translations.settingsClientDescription),
|
||||||
children: [
|
children: [
|
||||||
createFileSetting(
|
FileSetting(
|
||||||
key: settingsConsoleDllInputKey,
|
validatorKey: settingsConsoleDllInputKey,
|
||||||
title: translations.settingsClientConsoleName,
|
title: translations.settingsClientConsoleName,
|
||||||
description: translations.settingsClientConsoleDescription,
|
description: translations.settingsClientConsoleDescription,
|
||||||
controller: _dllController.unrealEngineConsoleDll,
|
controller: _dllController.unrealEngineConsoleDll,
|
||||||
@@ -73,10 +72,10 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
_dllController.unrealEngineConsoleDll.text = path;
|
_dllController.unrealEngineConsoleDll.text = path;
|
||||||
await _dllController.download(GameDll.console, path, force: true);
|
await _dllController.download(GameDll.console, path, force: true);
|
||||||
settingsConsoleDllInputKey.currentState?.validate();
|
settingsConsoleDllInputKey.currentState?.validate();
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
createFileSetting(
|
FileSetting(
|
||||||
key: settingsAuthDllInputKey,
|
validatorKey: settingsAuthDllInputKey,
|
||||||
title: translations.settingsClientAuthName,
|
title: translations.settingsClientAuthName,
|
||||||
description: translations.settingsClientAuthDescription,
|
description: translations.settingsClientAuthDescription,
|
||||||
controller: _dllController.backendDll,
|
controller: _dllController.backendDll,
|
||||||
@@ -85,10 +84,10 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
_dllController.backendDll.text = path;
|
_dllController.backendDll.text = path;
|
||||||
await _dllController.download(GameDll.auth, path, force: true);
|
await _dllController.download(GameDll.auth, path, force: true);
|
||||||
settingsAuthDllInputKey.currentState?.validate();
|
settingsAuthDllInputKey.currentState?.validate();
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
createFileSetting(
|
FileSetting(
|
||||||
key: settingsMemoryDllInputKey,
|
validatorKey: settingsMemoryDllInputKey,
|
||||||
title: translations.settingsClientMemoryName,
|
title: translations.settingsClientMemoryName,
|
||||||
description: translations.settingsClientMemoryDescription,
|
description: translations.settingsClientMemoryDescription,
|
||||||
controller: _dllController.memoryLeakDll,
|
controller: _dllController.memoryLeakDll,
|
||||||
@@ -97,7 +96,7 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
_dllController.memoryLeakDll.text = path;
|
_dllController.memoryLeakDll.text = path;
|
||||||
await _dllController.download(GameDll.memoryLeak, path, force: true);
|
await _dllController.download(GameDll.memoryLeak, path, force: true);
|
||||||
settingsAuthDllInputKey.currentState?.validate();
|
settingsAuthDllInputKey.currentState?.validate();
|
||||||
}
|
},
|
||||||
),
|
),
|
||||||
_gameServer
|
_gameServer
|
||||||
],
|
],
|
||||||
@@ -124,8 +123,6 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
subtitle: Text(translations.settingsServerTypeDescription),
|
subtitle: Text(translations.settingsServerTypeDescription),
|
||||||
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
contentWidth: SettingTile.kDefaultContentWidth + 30,
|
||||||
content: Obx(() => DropDownButton(
|
content: Obx(() => DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
|
leading: Text(_dllController.customGameServer.value ? translations.settingsServerTypeCustomName : translations.settingsServerTypeEmbeddedName),
|
||||||
items: {
|
items: {
|
||||||
false: translations.settingsServerTypeEmbeddedName,
|
false: translations.settingsServerTypeEmbeddedName,
|
||||||
@@ -200,8 +197,8 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}else {
|
}else {
|
||||||
return createFileSetting(
|
return FileSetting(
|
||||||
key: settingsGameServerDllInputKey,
|
validatorKey: settingsGameServerDllInputKey,
|
||||||
title: translations.settingsOldServerFileName,
|
title: translations.settingsOldServerFileName,
|
||||||
description: translations.settingsServerFileDescription,
|
description: translations.settingsServerFileDescription,
|
||||||
controller: _dllController.customGameServerDll,
|
controller: _dllController.customGameServerDll,
|
||||||
@@ -210,7 +207,7 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
_dllController.customGameServerDll.text = path;
|
_dllController.customGameServerDll.text = path;
|
||||||
await _dllController.download(GameDll.gameServer, path);
|
await _dllController.download(GameDll.gameServer, path);
|
||||||
settingsGameServerDllInputKey.currentState?.validate();
|
settingsGameServerDllInputKey.currentState?.validate();
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -295,8 +292,6 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
title: Text(translations.settingsUtilsLanguageName),
|
title: Text(translations.settingsUtilsLanguageName),
|
||||||
subtitle: Text(translations.settingsUtilsLanguageDescription),
|
subtitle: Text(translations.settingsUtilsLanguageDescription),
|
||||||
content: Obx(() => DropDownButton(
|
content: Obx(() => DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(_getLocaleName(_settingsController.language.value)),
|
leading: Text(_getLocaleName(_settingsController.language.value)),
|
||||||
items: AppLocalizations.supportedLocales.map((locale) => MenuFlyoutItem(
|
items: AppLocalizations.supportedLocales.map((locale) => MenuFlyoutItem(
|
||||||
text: Text(_getLocaleName(locale.languageCode)),
|
text: Text(_getLocaleName(locale.languageCode)),
|
||||||
@@ -321,8 +316,6 @@ class _SettingsPageState extends AbstractPageState<SettingsPage> {
|
|||||||
title: Text(translations.settingsUtilsThemeName),
|
title: Text(translations.settingsUtilsThemeName),
|
||||||
subtitle: Text(translations.settingsUtilsThemeDescription),
|
subtitle: Text(translations.settingsUtilsThemeDescription),
|
||||||
content: Obx(() => DropDownButton(
|
content: Obx(() => DropDownButton(
|
||||||
onOpen: () => inDialog = true,
|
|
||||||
onClose: () => inDialog = false,
|
|
||||||
leading: Text(_settingsController.themeMode.value.title),
|
leading: Text(_settingsController.themeMode.value.title),
|
||||||
items: ThemeMode.values.map((themeMode) => MenuFlyoutItem(
|
items: ThemeMode.values.map((themeMode) => MenuFlyoutItem(
|
||||||
text: Text(themeMode.title),
|
text: Text(themeMode.title),
|
||||||
@@ -3,25 +3,25 @@ import 'package:get/get.dart';
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
import 'package:reboot_launcher/src/controller/hosting_controller.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/message/profile.dart';
|
import 'package:reboot_launcher/src/message/profile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
|
|
||||||
class ProfileWidget extends StatefulWidget {
|
class ProfileTile extends StatefulWidget {
|
||||||
final GlobalKey<OverlayTargetState> overlayKey;
|
final GlobalKey<TutorialOverlayTargetState> overlayKey;
|
||||||
const ProfileWidget({required this.overlayKey});
|
const ProfileTile({required this.overlayKey});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ProfileWidget> createState() => _ProfileWidgetState();
|
State<ProfileTile> createState() => _ProfileTileState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ProfileWidgetState extends State<ProfileWidget> {
|
class _ProfileTileState extends State<ProfileTile> {
|
||||||
final GameController _gameController = Get.find<GameController>();
|
final GameController _gameController = Get.find<GameController>();
|
||||||
final HostingController _hostingController = Get.find<HostingController>();
|
final HostingController _hostingController = Get.find<HostingController>();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => OverlayTarget(
|
Widget build(BuildContext context) => TutorialOverlayTarget(
|
||||||
key: widget.overlayKey,
|
key: widget.overlayKey,
|
||||||
child: HoverButton(
|
child: HoverButton(
|
||||||
margin: const EdgeInsets.all(8.0),
|
margin: const EdgeInsets.all(8.0),
|
||||||
@@ -3,16 +3,16 @@ import 'dart:collection';
|
|||||||
|
|
||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:reboot_launcher/src/pager/page_type.dart';
|
import 'package:reboot_launcher/src/widget/section/host/page.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/section/info/page.dart';
|
||||||
import 'package:reboot_launcher/src/page/backend_page.dart';
|
import 'package:reboot_launcher/src/widget/section/settings/page.dart';
|
||||||
import 'package:reboot_launcher/src/page/browser_page.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar_area.dart';
|
||||||
import 'package:reboot_launcher/src/page/host_page.dart';
|
import 'package:reboot_launcher/src/widget/page/page_type.dart';
|
||||||
import 'package:reboot_launcher/src/page/info_page.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/pager/abstract_page.dart';
|
import 'package:reboot_launcher/src/widget/section/backend/page.dart';
|
||||||
import 'package:reboot_launcher/src/page/play_page.dart';
|
import 'package:reboot_launcher/src/widget/section/browser/page.dart';
|
||||||
import 'package:reboot_launcher/src/page/settings_page.dart';
|
import 'package:reboot_launcher/src/widget/page/abstract_page.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar_area.dart';
|
import 'package:reboot_launcher/src/widget/section/play/page.dart';
|
||||||
|
|
||||||
final StreamController<void> pagesController = StreamController.broadcast();
|
final StreamController<void> pagesController = StreamController.broadcast();
|
||||||
bool hitBack = false;
|
bool hitBack = false;
|
||||||
@@ -26,7 +26,7 @@ final List<AbstractPage> pages = [
|
|||||||
const SettingsPage()
|
const SettingsPage()
|
||||||
];
|
];
|
||||||
|
|
||||||
final List<GlobalKey<OverlayTargetState>> _flyoutPageControllers = List.generate(pages.length, (_) => GlobalKey());
|
final List<GlobalKey<TutorialOverlayTargetState>> _flyoutPageControllers = List.generate(pages.length, (_) => GlobalKey());
|
||||||
|
|
||||||
final RxInt pageIndex = RxInt(PageType.play.index);
|
final RxInt pageIndex = RxInt(PageType.play.index);
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ final GlobalKey<NavigatorState> appNavigatorKey = GlobalKey();
|
|||||||
|
|
||||||
final GlobalKey<OverlayState> appOverlayKey = GlobalKey();
|
final GlobalKey<OverlayState> appOverlayKey = GlobalKey();
|
||||||
|
|
||||||
final GlobalKey<InfoBarAreaState> infoBarAreaKey = GlobalKey();
|
final GlobalKey<SnackBarAreaState> infoBarAreaKey = GlobalKey();
|
||||||
|
|
||||||
GlobalKey get pageKey => getPageKeyByIndex(pageIndex.value);
|
GlobalKey get pageKey => getPageKeyByIndex(pageIndex.value);
|
||||||
|
|
||||||
@@ -82,6 +82,6 @@ void addSubPageToCurrent(String pageName) {
|
|||||||
pagesController.add(null);
|
pagesController.add(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalKey<OverlayTargetState> getOverlayTargetKeyByPage(int pageIndex) => _flyoutPageControllers[pageIndex];
|
GlobalKey<TutorialOverlayTargetState> getOverlayTargetKeyByPage(int pageIndex) => _flyoutPageControllers[pageIndex];
|
||||||
|
|
||||||
GlobalKey<OverlayTargetState> get pageOverlayTargetKey => _flyoutPageControllers[pageIndex.value];
|
GlobalKey<TutorialOverlayTargetState> get pageOverlayTargetKey => _flyoutPageControllers[pageIndex.value];
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:skeletons/skeletons.dart';
|
import 'package:skeletons/skeletons.dart';
|
||||||
|
|
||||||
class SettingTile extends StatefulWidget {
|
class SettingTile extends StatefulWidget {
|
||||||
@@ -78,7 +78,7 @@ class SettingTileState extends State<SettingTile> {
|
|||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
if(widget.overlayKey != null)
|
if(widget.overlayKey != null)
|
||||||
OverlayTarget(
|
TutorialOverlayTarget(
|
||||||
key: widget.overlayKey,
|
key: widget.overlayKey,
|
||||||
child: isSkeleton ? _skeletonIcon : icon,
|
child: isSkeleton ? _skeletonIcon : icon,
|
||||||
)
|
)
|
||||||
81
gui/lib/src/widget/snackbar/snackbar.dart
Normal file
81
gui/lib/src/widget/snackbar/snackbar.dart
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
|
|
||||||
|
const infoBarLongDuration = Duration(seconds: 4);
|
||||||
|
const infoBarShortDuration = Duration(seconds: 2);
|
||||||
|
const _height = 64.0;
|
||||||
|
|
||||||
|
class SnackBar {
|
||||||
|
static SnackBar open(String text, {
|
||||||
|
InfoBarSeverity severity = InfoBarSeverity.info,
|
||||||
|
bool loading = false,
|
||||||
|
Duration? duration = infoBarShortDuration,
|
||||||
|
void Function()? onDismissed,
|
||||||
|
Widget? action
|
||||||
|
}) {
|
||||||
|
final overlayEntry = SnackBar._internal(
|
||||||
|
overlay: ConstrainedBox(
|
||||||
|
constraints: BoxConstraints(
|
||||||
|
minHeight: _height
|
||||||
|
),
|
||||||
|
child: Mica(
|
||||||
|
elevation: 1,
|
||||||
|
child: InfoBar(
|
||||||
|
title: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: Text(text)
|
||||||
|
),
|
||||||
|
if(action != null)
|
||||||
|
action
|
||||||
|
],
|
||||||
|
),
|
||||||
|
isLong: false,
|
||||||
|
isIconVisible: true,
|
||||||
|
content: SizedBox(
|
||||||
|
width: double.infinity,
|
||||||
|
child: loading ? const Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 8.0,
|
||||||
|
bottom: 2.0,
|
||||||
|
right: 6.0
|
||||||
|
),
|
||||||
|
child: ProgressBar(),
|
||||||
|
) : const SizedBox()
|
||||||
|
),
|
||||||
|
severity: severity
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onDismissed: onDismissed
|
||||||
|
);
|
||||||
|
if(duration != null) {
|
||||||
|
Future.delayed(duration)
|
||||||
|
.then((_) => WidgetsBinding.instance.addPostFrameCallback((timeStamp) => overlayEntry.close()));
|
||||||
|
}
|
||||||
|
return overlayEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Widget overlay;
|
||||||
|
final void Function()? onDismissed;
|
||||||
|
|
||||||
|
SnackBar._internal({
|
||||||
|
required this.overlay,
|
||||||
|
required this.onDismissed
|
||||||
|
}) {
|
||||||
|
final context = pageKey.currentContext;
|
||||||
|
if(context != null) {
|
||||||
|
infoBarAreaKey.currentState?.insertChild(overlay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool close() {
|
||||||
|
final result = infoBarAreaKey.currentState?.removeChild(overlay) ?? false;
|
||||||
|
if(result) {
|
||||||
|
onDismissed?.call();
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
53
gui/lib/src/widget/snackbar/snackbar_area.dart
Normal file
53
gui/lib/src/widget/snackbar/snackbar_area.dart
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
|
|
||||||
|
class SnackBarArea extends StatefulWidget {
|
||||||
|
const SnackBarArea({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<SnackBarArea> createState() => SnackBarAreaState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class SnackBarAreaState extends State<SnackBarArea> {
|
||||||
|
final List<Widget> _children = [];
|
||||||
|
|
||||||
|
void insertChild(Widget child) {
|
||||||
|
setState(() {
|
||||||
|
_children.add(child);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool removeChild(Widget child) {
|
||||||
|
var result = false;
|
||||||
|
setState(() {
|
||||||
|
result = _children.remove(child);
|
||||||
|
});
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return StreamBuilder(
|
||||||
|
stream: pagesController.stream,
|
||||||
|
builder: (context, _) => Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
bottom: hasPageButton ? 72.0 : 16.0,
|
||||||
|
),
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: _snackbars,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Widget> get _snackbars => _children
|
||||||
|
.map((child) => _buildSnackbar(child))
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
Widget _buildSnackbar(Widget child) => Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 12.0),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,20 +1,22 @@
|
|||||||
import 'package:fluent_ui/fluent_ui.dart';
|
import 'package:fluent_ui/fluent_ui.dart';
|
||||||
import 'package:flutter/rendering.dart';
|
import 'package:flutter/rendering.dart';
|
||||||
import 'package:reboot_launcher/src/page/pages.dart';
|
import 'package:reboot_launcher/src/widget/sections.dart';
|
||||||
|
|
||||||
|
import 'tutorial_overlay_attach_mode.dart';
|
||||||
|
|
||||||
typedef WidgetBuilder = Widget Function(BuildContext, void Function());
|
typedef WidgetBuilder = Widget Function(BuildContext, void Function());
|
||||||
|
|
||||||
class OverlayTarget extends StatefulWidget {
|
class TutorialOverlayTarget extends StatefulWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
const OverlayTarget({super.key, required this.child});
|
const TutorialOverlayTarget({super.key, required this.child});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<OverlayTarget> createState() => OverlayTargetState();
|
State<TutorialOverlayTarget> createState() => TutorialOverlayTargetState();
|
||||||
|
|
||||||
OverlayTargetState of(BuildContext context) => context.findAncestorStateOfType<OverlayTargetState>()!;
|
TutorialOverlayTargetState of(BuildContext context) => context.findAncestorStateOfType<TutorialOverlayTargetState>()!;
|
||||||
}
|
}
|
||||||
|
|
||||||
class OverlayTargetState extends State<OverlayTarget> {
|
class TutorialOverlayTargetState extends State<TutorialOverlayTarget> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) => widget.child;
|
Widget build(BuildContext context) => widget.child;
|
||||||
|
|
||||||
@@ -23,7 +25,7 @@ class OverlayTargetState extends State<OverlayTarget> {
|
|||||||
required WidgetBuilder actionBuilder,
|
required WidgetBuilder actionBuilder,
|
||||||
Offset offset = Offset.zero,
|
Offset offset = Offset.zero,
|
||||||
bool ignoreTargetPointers = true,
|
bool ignoreTargetPointers = true,
|
||||||
AttachMode attachMode = AttachMode.start
|
TutorialOverlayAttachMode attachMode = TutorialOverlayAttachMode.start
|
||||||
}) {
|
}) {
|
||||||
final renderBox = context.findRenderObject() as RenderBox;
|
final renderBox = context.findRenderObject() as RenderBox;
|
||||||
final position = renderBox.localToGlobal(Offset.zero);
|
final position = renderBox.localToGlobal(Offset.zero);
|
||||||
@@ -40,10 +42,10 @@ class OverlayTargetState extends State<OverlayTarget> {
|
|||||||
)
|
)
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
left: position.dx - (attachMode != AttachMode.start ? renderBox.size.width : 0) + offset.dx,
|
left: position.dx - (attachMode != TutorialOverlayAttachMode.start ? renderBox.size.width : 0) + offset.dx,
|
||||||
top: position.dy + (renderBox.size.height / 2) + offset.dy,
|
top: position.dy + (renderBox.size.height / 2) + offset.dy,
|
||||||
child: CustomPaint(
|
child: CustomPaint(
|
||||||
painter: _CallOutShape(color, attachMode != AttachMode.start),
|
painter: _CallOutShape(color, attachMode != TutorialOverlayAttachMode.start),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(12.0),
|
padding: const EdgeInsets.all(12.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -65,12 +67,6 @@ class OverlayTargetState extends State<OverlayTarget> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum AttachMode {
|
|
||||||
start,
|
|
||||||
middle,
|
|
||||||
end;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Harder than one would think
|
// Harder than one would think
|
||||||
class _CallOutShape extends CustomPainter {
|
class _CallOutShape extends CustomPainter {
|
||||||
final Color color;
|
final Color color;
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
enum TutorialOverlayAttachMode {
|
||||||
|
start,
|
||||||
|
middle,
|
||||||
|
end;
|
||||||
|
}
|
||||||
@@ -7,10 +7,10 @@ import 'package:get/get.dart';
|
|||||||
import 'package:reboot_common/common.dart';
|
import 'package:reboot_common/common.dart';
|
||||||
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
import 'package:reboot_launcher/src/controller/game_controller.dart';
|
||||||
import 'package:reboot_launcher/src/util/translations.dart';
|
import 'package:reboot_launcher/src/util/translations.dart';
|
||||||
import 'package:reboot_launcher/src/tile/setting_tile.dart';
|
import 'package:reboot_launcher/src/widget/setting_tile.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
import 'package:reboot_launcher/src/widget/overlay/dialog.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/info_bar.dart';
|
import 'package:reboot_launcher/src/widget/snackbar/snackbar.dart';
|
||||||
import 'package:reboot_launcher/src/messenger/overlay.dart';
|
import 'package:reboot_launcher/src/widget/tutorial/tutorial_overlay.dart';
|
||||||
import 'package:reboot_launcher/src/message/download_version.dart';
|
import 'package:reboot_launcher/src/message/download_version.dart';
|
||||||
import 'package:reboot_launcher/src/message/import_version.dart';
|
import 'package:reboot_launcher/src/message/import_version.dart';
|
||||||
import 'package:url_launcher/url_launcher.dart';
|
import 'package:url_launcher/url_launcher.dart';
|
||||||
@@ -19,7 +19,7 @@ class VersionSelector extends StatefulWidget {
|
|||||||
const VersionSelector({Key? key}) : super(key: key);
|
const VersionSelector({Key? key}) : super(key: key);
|
||||||
|
|
||||||
static SettingTile buildTile({
|
static SettingTile buildTile({
|
||||||
required GlobalKey<OverlayTargetState> key
|
required GlobalKey<TutorialOverlayTargetState> key
|
||||||
}) => SettingTile(
|
}) => SettingTile(
|
||||||
icon: Icon(
|
icon: Icon(
|
||||||
FluentIcons.play_24_regular
|
FluentIcons.play_24_regular
|
||||||
@@ -221,7 +221,7 @@ class _VersionSelectorState extends State<VersionSelector> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool _onExplorerError() {
|
bool _onExplorerError() {
|
||||||
showRebootInfoBar(
|
SnackBar.open(
|
||||||
translations.missingVersionError,
|
translations.missingVersionError,
|
||||||
severity: InfoBarSeverity.error,
|
severity: InfoBarSeverity.error,
|
||||||
duration: infoBarLongDuration,
|
duration: infoBarLongDuration,
|
||||||
Reference in New Issue
Block a user