diff --git a/gui/lib/main.dart b/gui/lib/main.dart index 71ca6d9..28d58a1 100644 --- a/gui/lib/main.dart +++ b/gui/lib/main.dart @@ -9,6 +9,7 @@ import 'package:flutter_localized_locales/flutter_localized_locales.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:reboot_common/common.dart'; +import 'package:reboot_launcher/src/controller/info_controller.dart'; import 'package:reboot_launcher/src/controller/matchmaker_controller.dart'; import 'package:reboot_launcher/src/controller/update_controller.dart'; import 'package:reboot_launcher/src/dialog/abstract/info_bar.dart'; @@ -173,6 +174,7 @@ Future _initStorage() async { Get.put(BuildController()); Get.put(SettingsController()); Get.put(HostingController()); + Get.put(InfoController()); var updateController = UpdateController(); Get.put(updateController); updateController.update(); diff --git a/gui/lib/src/controller/build_controller.dart b/gui/lib/src/controller/build_controller.dart index fd46757..708a622 100644 --- a/gui/lib/src/controller/build_controller.dart +++ b/gui/lib/src/controller/build_controller.dart @@ -16,9 +16,4 @@ class BuildController extends GetxController { } selectedBuild.value = builds[0]; } - - void reset(){ - _builds = null; - selectedBuild.value = null; - } } diff --git a/gui/lib/src/controller/info_controller.dart b/gui/lib/src/controller/info_controller.dart new file mode 100644 index 0000000..67e47f1 --- /dev/null +++ b/gui/lib/src/controller/info_controller.dart @@ -0,0 +1,9 @@ +import 'package:get/get.dart'; +import 'package:reboot_common/common.dart'; + +class InfoController extends GetxController { + List? links; + Map linksData; + + InfoController() : linksData = {}; +} diff --git a/gui/lib/src/controller/settings_controller.dart b/gui/lib/src/controller/settings_controller.dart index f9278a4..3377142 100644 --- a/gui/lib/src/controller/settings_controller.dart +++ b/gui/lib/src/controller/settings_controller.dart @@ -6,6 +6,7 @@ import 'package:get_storage/get_storage.dart'; import 'package:intl/intl.dart'; import 'package:reboot_common/common.dart'; import 'package:reboot_launcher/main.dart'; +import 'package:reboot_launcher/src/util/translations.dart'; class SettingsController extends GetxController { late final GetStorage _storage; @@ -39,12 +40,10 @@ class SettingsController extends GetxController { firstRun.listen((value) => _storage.write("first_run", value)); themeMode = Rx(ThemeMode.values.elementAt(_storage.read("theme") ?? 0)); themeMode.listen((value) => _storage.write("theme", value.index)); - language = RxString(_storage.read("language") ?? _defaultLocale); + language = RxString(_storage.read("language") ?? currentLocale); language.listen((value) => _storage.write("language", value)); } - String get _defaultLocale => Intl.getCurrentLocale().split("_")[0]; - TextEditingController _createController(String key, String name) { var controller = TextEditingController(text: _storage.read(key) ?? _controllerDefaultPath(name)); controller.addListener(() => _storage.write(key, controller.text)); diff --git a/gui/lib/src/page/implementation/info_page.dart b/gui/lib/src/page/implementation/info_page.dart index 361c862..2e82647 100644 --- a/gui/lib/src/page/implementation/info_page.dart +++ b/gui/lib/src/page/implementation/info_page.dart @@ -1,11 +1,19 @@ +import 'dart:convert'; + import 'package:fluent_ui/fluent_ui.dart'; -import 'package:flutter/gestures.dart'; +import 'package:get/get.dart'; +import 'package:intl/intl.dart'; +import 'package:markdown_widget/widget/markdown.dart'; +import 'package:reboot_common/common.dart'; +import 'package:reboot_launcher/src/controller/info_controller.dart'; import 'package:reboot_launcher/src/page/abstract/page.dart'; import 'package:reboot_launcher/src/page/abstract/page_setting.dart'; import 'package:reboot_launcher/src/page/abstract/page_type.dart'; import 'package:reboot_launcher/src/util/translations.dart'; -import 'package:reboot_launcher/src/util/tutorial.dart'; import 'package:reboot_launcher/src/widget/common/setting_tile.dart'; +import 'package:http/http.dart' as http; +import 'package:skeletons/skeletons.dart'; +import 'package:url_launcher/url_launcher.dart'; class InfoPage extends RebootPage { const InfoPage({Key? key}) : super(key: key); @@ -30,101 +38,86 @@ class InfoPage extends RebootPage { } class _InfoPageState extends RebootPageState { - @override - Widget? get button => null; + final InfoController _infoController = Get.find(); + late Future> _fetchFuture; + late double _height; @override - List get settings => [ - SettingTile( - title: 'What is Project Reboot?', - subtitle: 'Project Reboot allows anyone to easily host a game server for most of Fortnite\'s seasons. ' - 'The project was started on Discord by Milxnor. ' - 'The project is no longer being actively maintained.', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'What is a game server?', - subtitle: 'When you join a Fortnite Game, your client connects to a game server that allows you to play with others. ' - 'You can join someone else\'s game server, or host one on your PC by going to the "Host" tab. ', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'What is a client?', - subtitle: 'A client is the actual Fortnite game. ' - 'You can download any version of Fortnite from the launcher in the "Play" tab. ' - 'You can also import versions from your local PC, but remember that these may be corrupted. ' - 'If a local version doesn\'t work, try installing it from the launcher before reporting a bug.', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'What is an authenticator?', - subtitle: 'An authenticator is a program that handles authentication, parties and voice chats. ' - 'By default, a LawinV1 server will be started for you to play. ' - 'You can use also use an authenticator running locally(on your PC) or remotely(on another PC). ' - 'Changing the authenticator settings can break the client and game server: unless you are an advanced user, do not edit, for any reason, these settings! ' - 'If you need to restore these settings, go to the "Settings" tab and click on "Restore Defaults". ', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'Do I need to update DLLs?', - subtitle: 'No, all the files that the launcher uses are automatically updated. ' - 'You can use your own DLLs by going to the "Settings" tab, but make sure that they don\'t create a console that reads IO or the launcher will stop working correctly. ' - 'Unless you are an advanced user, changing these options is not recommended', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'Where can I report bugs or ask for new features?', - subtitle: 'Go to the "Settings" tab and click on report bug. ' - 'Please make sure to be as specific as possible when filing a report as it\'s crucial to make it as easy to fix/implement', - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ), - SettingTile( - title: 'How can I make my game server accessible for other players?', - subtitle: Text.rich( - TextSpan( - children: [ - TextSpan( - text: 'Follow ', - style: FluentTheme.of(context).typography.body - ), - TextSpan( - text: 'this tutorial', - mouseCursor: SystemMouseCursors.click, - style: FluentTheme.of(context).typography.body?.copyWith(color: FluentTheme.of(context).accentColor), - recognizer: TapGestureRecognizer()..onTap = openPortTutorial - ) - ] - ) - ), - titleStyle: FluentTheme - .of(context) - .typography - .title, - contentWidth: null, - ) - ]; + void initState() { + _fetchFuture = _infoController.links != null + ? Future.value(_infoController.links) + : _initQuery(); + super.initState(); + } + + Future> _initQuery() async { + var response = await http.get(Uri.parse("https://api.github.com/repos/Auties00/reboot_launcher/contents/documentation/$currentLocale")); + List results = jsonDecode(response.body) + .map((entry) => entry["download_url"] as String) + .toList(); + return _infoController.links = results; + } + + Future _readLink(String url) async { + var known = _infoController.linksData[url]; + if(known != null) { + return known; + } + + var response = await http.get(Uri.parse(url)); + return _infoController.linksData[url] = response.body; + } + + @override + Widget build(BuildContext context) { + super.build(context); + _height = MediaQuery.of(context).size.height / 3; + return FutureBuilder( + future: _fetchFuture, + builder: (context, linksSnapshot) { + var linksData = linksSnapshot.data; + return ListView.builder( + itemBuilder: (context, index) { + if (index % 2 == 0) { + return const SizedBox( + height: 16.0 + ); + } + + return Card( + borderRadius: const BorderRadius.vertical(top: Radius.circular(4.0)), + child: _buildBody(linksData, index) + ); + }, + itemCount: linksData == null ? null : linksData.length * 2 + ); + } + ); + } + + Widget _buildBody(List? linksData, int index) { + if (linksData == null) { + return SkeletonLine( + style: SkeletonLineStyle( + height: _height + ), + ); + } + + return FutureBuilder( + future: _readLink(linksData[index ~/ 2]), + builder: (context, linkDataSnapshot) => SizedBox( + height: _height, + child: MarkdownWidget( + data: linkDataSnapshot.data ?? "" + ), + ) + ); + } + + @override + List get settings => []; + + @override + Widget? get button => null; } \ No newline at end of file diff --git a/gui/lib/src/util/translations.dart b/gui/lib/src/util/translations.dart index 0cbb861..12ff042 100644 --- a/gui/lib/src/util/translations.dart +++ b/gui/lib/src/util/translations.dart @@ -1,5 +1,6 @@ import 'package:fluent_ui/fluent_ui.dart'; import 'package:flutter_gen/gen_l10n/reboot_localizations.dart'; +import 'package:intl/intl.dart'; AppLocalizations? _translations; bool _init = false; @@ -15,4 +16,6 @@ AppLocalizations get translations { void loadTranslations(BuildContext context) { _translations = AppLocalizations.of(context)!; _init = true; -} \ No newline at end of file +} + +String get currentLocale => Intl.getCurrentLocale().split("_")[0]; diff --git a/gui/pubspec.yaml b/gui/pubspec.yaml index 86e4487..c362d3a 100644 --- a/gui/pubspec.yaml +++ b/gui/pubspec.yaml @@ -45,6 +45,7 @@ dependencies: intl: any windows_taskbar: ^1.1.2 flutter_localized_locales: ^2.0.5 + markdown_widget: ^2.2.0 dependency_overrides: xml: ^6.3.0