mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 19:22:22 +01:00
9.1.4
This commit is contained in:
@@ -29,6 +29,8 @@ import 'package:reboot_launcher/src/widget/profile_tile.dart';
|
||||
import 'package:reboot_launcher/src/widget/title_bar.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'info_page.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@@ -221,6 +223,7 @@ class _HomePageState extends State<HomePage> with WindowListener, AutomaticKeepA
|
||||
super.build(context);
|
||||
_settingsController.language.value;
|
||||
loadTranslations(context);
|
||||
// InfoPage.initInfoTiles();
|
||||
return Obx(() {
|
||||
return NavigationPaneTheme(
|
||||
data: NavigationPaneThemeData(
|
||||
@@ -394,7 +397,7 @@ class _PaneBody extends StatefulWidget {
|
||||
|
||||
class _PaneBodyState extends State<_PaneBody> with AutomaticKeepAliveClientMixin {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
final PageController _pageController = PageController(keepPage: true);
|
||||
final PageController _pageController = PageController(keepPage: true, initialPage: pageIndex.value);
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
@@ -402,7 +405,15 @@ class _PaneBodyState extends State<_PaneBody> with AutomaticKeepAliveClientMixin
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
pageIndex.listen((index) => _pageController.jumpToPage(index));
|
||||
var lastPage = pageIndex.value;
|
||||
pageIndex.listen((index) {
|
||||
if(index == lastPage) {
|
||||
return;
|
||||
}
|
||||
|
||||
lastPage = index;
|
||||
_pageController.jumpToPage(index);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -437,6 +448,10 @@ class _PaneBodyState extends State<_PaneBody> with AutomaticKeepAliveClientMixin
|
||||
elements.add(TextSpan(
|
||||
text: pages[pageIndex.value].name,
|
||||
recognizer: pageStack.isNotEmpty ? (TapGestureRecognizer()..onTap = () {
|
||||
if(inDialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(var i = 0; i < pageStack.length; i++) {
|
||||
Navigator.of(pageKey.currentContext!).pop();
|
||||
final element = pageStack.removeLast();
|
||||
@@ -461,6 +476,10 @@ class _PaneBodyState extends State<_PaneBody> with AutomaticKeepAliveClientMixin
|
||||
elements.add(TextSpan(
|
||||
text: innerPage,
|
||||
recognizer: i == pageStack.length - 1 ? null : (TapGestureRecognizer()..onTap = () {
|
||||
if(inDialog) {
|
||||
return;
|
||||
}
|
||||
|
||||
for(var j = 0; j < i - 1; j++) {
|
||||
Navigator.of(pageKey.currentContext!).pop();
|
||||
final element = pageStack.removeLast();
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import 'dart:async';
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page.dart';
|
||||
import 'package:reboot_launcher/src/page/abstract/page_type.dart';
|
||||
import 'package:reboot_launcher/src/page/pages.dart';
|
||||
@@ -15,12 +14,14 @@ import 'package:path/path.dart' as path;
|
||||
import 'package:reboot_launcher/src/widget/info_tile.dart';
|
||||
|
||||
class InfoPage extends RebootPage {
|
||||
static late final List<InfoTile> _infoTiles;
|
||||
static late List<InfoTile> _infoTiles;
|
||||
static late List<_QuizEntry> _quizEntries;
|
||||
|
||||
static Object? initInfoTiles() {
|
||||
try {
|
||||
final directory = Directory("${assetsDirectory.path}\\info\\$currentLocale");
|
||||
final map = SplayTreeMap<int, InfoTile>();
|
||||
for(final entry in directory.listSync()) {
|
||||
final faqDirectory = Directory("${assetsDirectory.path}\\info\\$currentLocale\\faq");
|
||||
final infoTiles = SplayTreeMap<int, InfoTile>();
|
||||
for(final entry in faqDirectory.listSync()) {
|
||||
if(entry is File) {
|
||||
final name = Uri.decodeQueryComponent(path.basename(entry.path));
|
||||
final splitter = name.indexOf(".");
|
||||
@@ -34,16 +35,42 @@ class InfoPage extends RebootPage {
|
||||
}
|
||||
|
||||
final questionName = Uri.decodeQueryComponent(name.substring(splitter + 2));
|
||||
map[index] = InfoTile(
|
||||
infoTiles[index] = InfoTile(
|
||||
title: Text(questionName),
|
||||
content: Text(entry.readAsStringSync())
|
||||
);
|
||||
}
|
||||
}
|
||||
_infoTiles = map.values.toList(growable: false);
|
||||
_infoTiles = infoTiles.values.toList(growable: false);
|
||||
|
||||
final questionsDirectory = Directory("${assetsDirectory.path}\\info\\$currentLocale\\questions");
|
||||
final questions = SplayTreeMap<int, _QuizEntry>();
|
||||
for(final entry in questionsDirectory.listSync()) {
|
||||
if(entry is File) {
|
||||
final name = Uri.decodeQueryComponent(path.basename(entry.path));
|
||||
final splitter = name.indexOf(".");
|
||||
if(splitter == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final index = int.tryParse(name.substring(0, splitter));
|
||||
if(index == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
final questionName = Uri.decodeQueryComponent(name.substring(splitter + 2));
|
||||
questions[index] = _QuizEntry(
|
||||
question: questionName,
|
||||
options: entry.readAsStringSync().split("\n")
|
||||
);
|
||||
}
|
||||
}
|
||||
_quizEntries = questions.values.toList(growable: false);
|
||||
|
||||
return null;
|
||||
}catch(error) {
|
||||
_infoTiles = [];
|
||||
_quizEntries = [];
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -60,7 +87,7 @@ class InfoPage extends RebootPage {
|
||||
String get iconAsset => "assets/images/info.png";
|
||||
|
||||
@override
|
||||
bool hasButton(String? routeName) => false;
|
||||
bool hasButton(String? pageName) => Get.find<SettingsController>().firstRun.value && pageName != null;
|
||||
|
||||
@override
|
||||
RebootPageType get type => RebootPageType.info;
|
||||
@@ -68,22 +95,14 @@ class InfoPage extends RebootPage {
|
||||
|
||||
class _InfoPageState extends RebootPageState<InfoPage> {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
RxInt _counter = RxInt(kDebugMode ? 0 : 180);
|
||||
late bool _showButton;
|
||||
late final Rxn<Widget> _quizPage;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
_showButton = _settingsController.firstRun.value;
|
||||
if(_settingsController.firstRun.value) {
|
||||
Timer.periodic(const Duration(seconds: 1), (timer) {
|
||||
if (_counter.value <= 0) {
|
||||
_settingsController.firstRun.value = false;
|
||||
timer.cancel();
|
||||
} else {
|
||||
_counter.value = _counter.value - 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
_quizPage = Rxn(_settingsController.firstRun.value ? _QuizRoute(
|
||||
entries: InfoPage._quizEntries,
|
||||
onSuccess: () => _quizPage.value = null
|
||||
) : null);
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@@ -92,28 +111,183 @@ class _InfoPageState extends RebootPageState<InfoPage> {
|
||||
|
||||
@override
|
||||
Widget? get button {
|
||||
if(!_showButton) {
|
||||
return const SizedBox.shrink();
|
||||
if(_quizPage.value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return Obx(() {
|
||||
final totalSecondsLeft = _counter.value;
|
||||
final minutesLeft = totalSecondsLeft ~/ 60;
|
||||
final secondsLeft = totalSecondsLeft % 60;
|
||||
final page = _quizPage.value;
|
||||
if(page == null) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
child: Button(
|
||||
onPressed: totalSecondsLeft <= 0 ? () {
|
||||
_showButton = false;
|
||||
pageIndex.value = RebootPageType.play.index;
|
||||
} : null,
|
||||
onPressed: () => Navigator.of(context).push(PageRouteBuilder(
|
||||
transitionDuration: Duration.zero,
|
||||
reverseTransitionDuration: Duration.zero,
|
||||
settings: RouteSettings(
|
||||
name: translations.quiz
|
||||
),
|
||||
pageBuilder: (context, incoming, outgoing) => page
|
||||
)),
|
||||
child: Text(
|
||||
totalSecondsLeft <= 0 ? "I have read the instructions"
|
||||
: "Read the instructions for at least ${secondsLeft == 0 ? '$minutesLeft minute${minutesLeft > 1 ? 's' : ''}' : minutesLeft == 0 ? '$secondsLeft second${secondsLeft > 1 ? 's' : ''}' : '$minutesLeft minute${minutesLeft > 1 ? 's' : ''} and $secondsLeft second${secondsLeft > 1 ? 's' : ''}'}"
|
||||
translations.startQuiz
|
||||
),
|
||||
)
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _QuizRoute extends StatefulWidget {
|
||||
final List<_QuizEntry> entries;
|
||||
final void Function() onSuccess;
|
||||
const _QuizRoute({
|
||||
required this.entries,
|
||||
required this.onSuccess
|
||||
});
|
||||
|
||||
@override
|
||||
State<_QuizRoute> createState() => _QuizRouteState();
|
||||
}
|
||||
|
||||
class _QuizRouteState extends State<_QuizRoute> with AutomaticKeepAliveClientMixin {
|
||||
final SettingsController _settingsController = Get.find<SettingsController>();
|
||||
late final List<RxInt> _selectedIndexes = List.generate(widget.entries.length, (_) => RxInt(-1));
|
||||
int _triesLeft = 3;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
children: widget.entries.indexed.expand((entry) {
|
||||
final selectedIndex = _selectedIndexes[entry.$1];
|
||||
return [
|
||||
Text(
|
||||
"${entry.$1 + 1}. ${entry.$2.question}",
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.w600
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12.0),
|
||||
...entry.$2.options.indexed.map<Widget>((value) => Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12.0),
|
||||
child: Obx(() => RadioButton(
|
||||
checked: value.$1 == selectedIndex.value,
|
||||
content: Text(value.$2, textAlign: TextAlign.center),
|
||||
onChanged: (_) => selectedIndex.value = value.$1
|
||||
)),
|
||||
)),
|
||||
const SizedBox(height: 12.0)
|
||||
];
|
||||
}).toList()
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
height: 48,
|
||||
child: Obx(() {
|
||||
var clickable = true;
|
||||
for(final index in _selectedIndexes) {
|
||||
if(index.value == -1) {
|
||||
clickable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Button(
|
||||
onPressed: clickable ? () async {
|
||||
if(_triesLeft <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var right = 0;
|
||||
final total = widget.entries.length;
|
||||
for(var i = 0; i < total; i++) {
|
||||
final selectedIndex = _selectedIndexes[i].value;
|
||||
final correctIndex = widget.entries[i].correctIndex;
|
||||
if(selectedIndex == correctIndex) {
|
||||
right++;
|
||||
}
|
||||
}
|
||||
|
||||
if(right == total) {
|
||||
widget.onSuccess();
|
||||
showInfoBar(
|
||||
translations.quizSuccess,
|
||||
severity: InfoBarSeverity.success
|
||||
);
|
||||
_settingsController.firstRun.value = false;
|
||||
Navigator.of(context).pop();
|
||||
pageIndex.value = RebootPageType.play.index;
|
||||
return;
|
||||
}
|
||||
|
||||
switch(--_triesLeft) {
|
||||
case 0:
|
||||
showInfoBar(
|
||||
translations.quizFailed(
|
||||
right,
|
||||
total,
|
||||
translations.quizZeroTriesLeft
|
||||
),
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
await Future.delayed(const Duration(seconds: 1));
|
||||
exit(0);
|
||||
case 1:
|
||||
showInfoBar(
|
||||
translations.quizFailed(
|
||||
right,
|
||||
total,
|
||||
translations.quizOneTryLeft
|
||||
),
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
showInfoBar(
|
||||
translations.quizFailed(
|
||||
right,
|
||||
total,
|
||||
translations.quizTwoTriesLeft
|
||||
),
|
||||
severity: InfoBarSeverity.error
|
||||
);
|
||||
break;
|
||||
}
|
||||
} : null,
|
||||
child: Text(translations.checkQuiz),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _QuizEntry {
|
||||
final String question;
|
||||
final List<String> options;
|
||||
late final int correctIndex;
|
||||
|
||||
_QuizEntry({required this.question, required this.options}) {
|
||||
final correct = options.first;
|
||||
options.shuffle();
|
||||
correctIndex = options.indexOf(correct);
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,7 @@ class HostPage extends RebootPage {
|
||||
const HostPage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
String get name => "Host";
|
||||
String get name => translations.hostName;
|
||||
|
||||
@override
|
||||
String get iconAsset => "assets/images/host.png";
|
||||
@@ -289,51 +289,23 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
title: Text(translations.settingsServerOptionsName),
|
||||
subtitle: Text(translations.settingsServerOptionsSubtitle),
|
||||
children: [
|
||||
Obx(() => SettingTile(
|
||||
SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.window_console_20_regular
|
||||
),
|
||||
title: Text(translations.hostHeadlessName),
|
||||
subtitle: Text(translations.hostHeadlessDescription),
|
||||
contentWidth: null,
|
||||
content: Row(
|
||||
children: [
|
||||
Text(
|
||||
_hostingController.headless.value ? translations.on : translations.off
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
ToggleSwitch(
|
||||
checked: _hostingController.headless.value,
|
||||
onChanged: (value) => _hostingController.headless.value = value
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
Obx(() => SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.desktop_edit_24_regular
|
||||
),
|
||||
title: Text(translations.hostVirtualDesktopName),
|
||||
subtitle: Text(translations.hostVirtualDesktopDescription),
|
||||
contentWidth: null,
|
||||
content: Row(
|
||||
children: [
|
||||
Text(
|
||||
_hostingController.virtualDesktop.value ? translations.on : translations.off
|
||||
),
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
ToggleSwitch(
|
||||
checked: _hostingController.virtualDesktop.value,
|
||||
onChanged: (value) => _hostingController.virtualDesktop.value = value
|
||||
),
|
||||
],
|
||||
),
|
||||
)),
|
||||
Obx(() => SettingTile(
|
||||
title: Text(translations.gameServerTypeName),
|
||||
subtitle: Text(translations.gameServerTypeDescription),
|
||||
content: Obx(() => DropDownButton(
|
||||
onOpen: () => inDialog = true,
|
||||
onClose: () => inDialog = false,
|
||||
leading: Text(_hostingController.type.value.translatedName),
|
||||
items: GameServerType.values.map((entry) => MenuFlyoutItem(
|
||||
text: Text(entry.translatedName),
|
||||
onPressed: () => _hostingController.type.value = entry
|
||||
)).toList()
|
||||
)),
|
||||
),
|
||||
SettingTile(
|
||||
icon: Icon(
|
||||
FluentIcons.arrow_reset_24_regular
|
||||
),
|
||||
@@ -348,13 +320,13 @@ class _HostingPageState extends RebootPageState<HostPage> {
|
||||
const SizedBox(
|
||||
width: 16.0
|
||||
),
|
||||
ToggleSwitch(
|
||||
Obx(() => ToggleSwitch(
|
||||
checked: _hostingController.autoRestart.value,
|
||||
onChanged: (value) => _hostingController.autoRestart.value = value
|
||||
),
|
||||
)),
|
||||
],
|
||||
),
|
||||
)),
|
||||
),
|
||||
SettingTile(
|
||||
icon: Icon(
|
||||
fluentUi.FluentIcons.number_field
|
||||
|
||||
Reference in New Issue
Block a user