From 89c3835d313dcbccc9e636b1f587bb56aba2fdf4 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 02:13:25 +0800 Subject: [PATCH 01/12] MCSI-1 feat: remove template --- lib/main.dart | 122 ------------------ .../ui/minecraft_server_installer.dart | 10 ++ lib/main/main.dart | 6 + test/widget_test.dart | 24 +--- 4 files changed, 17 insertions(+), 145 deletions(-) delete mode 100644 lib/main.dart create mode 100644 lib/main/framework/ui/minecraft_server_installer.dart create mode 100644 lib/main/main.dart diff --git a/lib/main.dart b/lib/main.dart deleted file mode 100644 index 7b7f5b6..0000000 --- a/lib/main.dart +++ /dev/null @@ -1,122 +0,0 @@ -import 'package:flutter/material.dart'; - -void main() { - runApp(const MyApp()); -} - -class MyApp extends StatelessWidget { - const MyApp({super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return MaterialApp( - title: 'Flutter Demo', - theme: ThemeData( - // This is the theme of your application. - // - // TRY THIS: Try running your application with "flutter run". You'll see - // the application has a purple toolbar. Then, without quitting the app, - // try changing the seedColor in the colorScheme below to Colors.green - // and then invoke "hot reload" (save your changes or press the "hot - // reload" button in a Flutter-supported IDE, or press "r" if you used - // the command line to start the app). - // - // Notice that the counter didn't reset back to zero; the application - // state is not lost during the reload. To reset the state, use hot - // restart instead. - // - // This works for code too, not just values: Most code changes can be - // tested with just a hot reload. - colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple), - ), - home: const MyHomePage(title: 'Flutter Demo Home Page'), - ); - } -} - -class MyHomePage extends StatefulWidget { - const MyHomePage({super.key, required this.title}); - - // This widget is the home page of your application. It is stateful, meaning - // that it has a State object (defined below) that contains fields that affect - // how it looks. - - // This class is the configuration for the state. It holds the values (in this - // case the title) provided by the parent (in this case the App widget) and - // used by the build method of the State. Fields in a Widget subclass are - // always marked "final". - - final String title; - - @override - State createState() => _MyHomePageState(); -} - -class _MyHomePageState extends State { - int _counter = 0; - - void _incrementCounter() { - setState(() { - // This call to setState tells the Flutter framework that something has - // changed in this State, which causes it to rerun the build method below - // so that the display can reflect the updated values. If we changed - // _counter without calling setState(), then the build method would not be - // called again, and so nothing would appear to happen. - _counter++; - }); - } - - @override - Widget build(BuildContext context) { - // This method is rerun every time setState is called, for instance as done - // by the _incrementCounter method above. - // - // The Flutter framework has been optimized to make rerunning build methods - // fast, so that you can just rebuild anything that needs updating rather - // than having to individually change instances of widgets. - return Scaffold( - appBar: AppBar( - // TRY THIS: Try changing the color here to a specific color (to - // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar - // change color while the other colors stay the same. - backgroundColor: Theme.of(context).colorScheme.inversePrimary, - // Here we take the value from the MyHomePage object that was created by - // the App.build method, and use it to set our appbar title. - title: Text(widget.title), - ), - body: Center( - // Center is a layout widget. It takes a single child and positions it - // in the middle of the parent. - child: Column( - // Column is also a layout widget. It takes a list of children and - // arranges them vertically. By default, it sizes itself to fit its - // children horizontally, and tries to be as tall as its parent. - // - // Column has various properties to control how it sizes itself and - // how it positions its children. Here we use mainAxisAlignment to - // center the children vertically; the main axis here is the vertical - // axis because Columns are vertical (the cross axis would be - // horizontal). - // - // TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint" - // action in the IDE, or press "p" in the console), to see the - // wireframe for each widget. - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text('You have pushed the button this many times:'), - Text( - '$_counter', - style: Theme.of(context).textTheme.headlineMedium, - ), - ], - ), - ), - floatingActionButton: FloatingActionButton( - onPressed: _incrementCounter, - tooltip: 'Increment', - child: const Icon(Icons.add), - ), // This trailing comma makes auto-formatting nicer for build methods. - ); - } -} diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart new file mode 100644 index 0000000..9435b8d --- /dev/null +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -0,0 +1,10 @@ +import 'package:flutter/material.dart'; + +class MinecraftServerInstaller extends StatelessWidget { + const MinecraftServerInstaller({super.key}); + + @override + Widget build(BuildContext context) { + return const Placeholder(); + } +} \ No newline at end of file diff --git a/lib/main/main.dart b/lib/main/main.dart new file mode 100644 index 0000000..96db365 --- /dev/null +++ b/lib/main/main.dart @@ -0,0 +1,6 @@ +import 'package:flutter/material.dart'; +import 'package:minecraft_server_installer/main/framework/ui/minecraft_server_installer.dart'; + +void main() { + runApp(const MinecraftServerInstaller()); +} diff --git a/test/widget_test.dart b/test/widget_test.dart index 063aa55..2a2b819 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -5,26 +5,4 @@ // gestures. You can also use WidgetTester to find child widgets in the widget // tree, read text, and verify that the values of widget properties are correct. -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:minecraft_server_installer/main.dart'; - -void main() { - testWidgets('Counter increments smoke test', (WidgetTester tester) async { - // Build our app and trigger a frame. - await tester.pumpWidget(const MyApp()); - - // Verify that our counter starts at 0. - expect(find.text('0'), findsOneWidget); - expect(find.text('1'), findsNothing); - - // Tap the '+' icon and trigger a frame. - await tester.tap(find.byIcon(Icons.add)); - await tester.pump(); - - // Verify that our counter has incremented. - expect(find.text('0'), findsNothing); - expect(find.text('1'), findsOneWidget); - }); -} +void main() {} From 3be665c9f9f8726faec7dc06d63995cbb5645aca Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 02:20:06 +0800 Subject: [PATCH 02/12] MCSI-1 feat: update description --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index e2dd2e9..fcd7c78 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: minecraft_server_installer -description: "A new Flutter project." +description: "A tool that makes installing a Minecraft Server easier." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. publish_to: 'none' # Remove this line if you wish to publish to pub.dev From ecee7ac16fd0df79ff7c1186e04da4ce8d6745f2 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 02:54:21 +0800 Subject: [PATCH 03/12] MCSI-1 feat: update gitignore --- .gitignore | 49 ++------------------------- .idea/libraries/Dart_SDK.xml | 19 +++++++++++ .idea/libraries/KotlinJavaRuntime.xml | 15 ++++++++ .idea/modules.xml | 9 +++++ .idea/runConfigurations/main_dart.xml | 6 ++++ .idea/workspace.xml | 36 ++++++++++++++++++++ .vscode/launch.json | 28 +++++++++++++++ .vscode/settings.json | 3 ++ minecraft_server_installer.iml | 17 ++++++++++ 9 files changed, 135 insertions(+), 47 deletions(-) create mode 100644 .idea/libraries/Dart_SDK.xml create mode 100644 .idea/libraries/KotlinJavaRuntime.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/runConfigurations/main_dart.xml create mode 100644 .idea/workspace.xml create mode 100644 .vscode/launch.json create mode 100644 .vscode/settings.json create mode 100644 minecraft_server_installer.iml diff --git a/.gitignore b/.gitignore index cd4d4b3..64b652e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,52 +4,19 @@ *.log *.pyc *.swp -.DS_Store -.atom/ .buildlog/ .history -.svn/ - -# As packages are no longer pinned, we use a lockfile for testing locally. -# When unpinning packages, Using lockfiles ensures that failures in PRs are -# actually due to those PRs, not due to a package being updated. -!/pubspec.lock - -# IntelliJ related -*.iml -*.ipr -*.iws -.idea/ - -# Visual Studio Code related -.classpath -.project -.settings/ -.vscode/* -.ccls-cache - -# This file, on the master branch, should never exist or be checked-in. -# -# On a *final* release branch, that is, what will ship to stable or beta, the -# file can be force added (git add --force) and checked-in in order to effectively -# "pin" the engine artifact version so the flutter tool does not need to use git -# to determine the engine artifacts. -# -# See https://github.com/flutter/flutter/blob/main/docs/tool/Engine-artifacts.md. -/bin/internal/engine.version # Flutter repo-specific /bin/cache/ /bin/internal/bootstrap.bat /bin/internal/bootstrap.sh -/bin/internal/engine.realm /bin/mingit/ /dev/benchmarks/mega_gallery/ /dev/bots/.recipe_deps /dev/bots/android_tools/ /dev/devicelab/ABresults*.json /dev/docs/doc/ -/dev/docs/api_docs.zip /dev/docs/flutter.docs.zip /dev/docs/lib/ /dev/docs/pubspec.yaml @@ -65,11 +32,11 @@ analysis_benchmark.json # Flutter/Dart/Pub related **/doc/api/ .dart_tool/ +.flutter-plugins .flutter-plugins-dependencies **/generated_plugin_registrant.dart .packages .pub-preload-cache/ -.pub-cache/ .pub/ build/ flutter_*.png @@ -83,11 +50,10 @@ unlinked_spec.ds **/android/captures/ **/android/gradlew **/android/gradlew.bat +**/android/local.properties **/android/**/GeneratedPluginRegistrant.java **/android/key.properties *.jks -local.properties -**/.cxx/ # iOS/XCode related **/ios/**/*.mode1v3 @@ -127,13 +93,11 @@ local.properties **/xcuserdata/ # Windows -**/windows/flutter/ephemeral/ **/windows/flutter/generated_plugin_registrant.cc **/windows/flutter/generated_plugin_registrant.h **/windows/flutter/generated_plugins.cmake # Linux -**/linux/flutter/ephemeral/ **/linux/flutter/generated_plugin_registrant.cc **/linux/flutter/generated_plugin_registrant.h **/linux/flutter/generated_plugins.cmake @@ -151,15 +115,6 @@ app.*.symbols !**/ios/**/default.perspectivev3 !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages !/dev/ci/**/Gemfile.lock -!.vscode/settings.json - -# Monorepo -.cipd -.gclient -.gclient_entries -.python-version -.gclient_previous_custom_vars -.gclient_previous_sync_commits # FVM Version Cache .fvm/ \ No newline at end of file diff --git a/.idea/libraries/Dart_SDK.xml b/.idea/libraries/Dart_SDK.xml new file mode 100644 index 0000000..6a7e7a7 --- /dev/null +++ b/.idea/libraries/Dart_SDK.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/libraries/KotlinJavaRuntime.xml b/.idea/libraries/KotlinJavaRuntime.xml new file mode 100644 index 0000000..2b96ac4 --- /dev/null +++ b/.idea/libraries/KotlinJavaRuntime.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b7f01cc --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/.idea/runConfigurations/main_dart.xml b/.idea/runConfigurations/main_dart.xml new file mode 100644 index 0000000..aab7b5c --- /dev/null +++ b/.idea/runConfigurations/main_dart.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..5b3388c --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..0429863 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,28 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "minecraft-server-installer", + "request": "launch", + "type": "dart", + "program": "lib/main/main.dart" + }, + { + "name": "minecraft-server-installer (profile mode)", + "request": "launch", + "type": "dart", + "flutterMode": "profile", + "program": "lib/main/main.dart" + }, + { + "name": "minecraft-server-installer (release mode)", + "request": "launch", + "type": "dart", + "flutterMode": "release", + "program": "lib/main/main.dart" + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f4607f2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "dart.lineLength": 120 +} diff --git a/minecraft_server_installer.iml b/minecraft_server_installer.iml new file mode 100644 index 0000000..f66303d --- /dev/null +++ b/minecraft_server_installer.iml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + From bdad1ae8b6986c2aca7cb7a634dbd4c05bf8ad92 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 04:59:49 +0800 Subject: [PATCH 04/12] MCSI-1 feat: version selection --- analysis_options.yaml | 3 +- .../framework/ui/basic_configuration_tab.dart | 19 +++++ .../ui/minecraft_server_installer.dart | 11 ++- lib/main/framework/ui/strings.dart | 3 + .../gateway/game_version_api_service.dart | 5 ++ .../gateway/game_version_repository_impl.dart | 12 ++++ .../presentation/game_version_bloc.dart | 22 ++++++ .../presentation/game_version_view_model.dart | 14 ++++ .../repository/game_version_repository.dart | 5 ++ .../get_game_version_list_use_case.dart | 10 +++ lib/vanila/domain/entity/game_version.dart | 11 +++ .../api/game_version_api_service_impl.dart | 20 ++++++ .../framework/ui/game_version_dropdown.dart | 64 +++++++++++++++++ pubspec.lock | 72 +++++++++++++++++++ pubspec.yaml | 3 + 15 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 lib/main/framework/ui/basic_configuration_tab.dart create mode 100644 lib/main/framework/ui/strings.dart create mode 100644 lib/vanila/adapter/gateway/game_version_api_service.dart create mode 100644 lib/vanila/adapter/gateway/game_version_repository_impl.dart create mode 100644 lib/vanila/adapter/presentation/game_version_bloc.dart create mode 100644 lib/vanila/adapter/presentation/game_version_view_model.dart create mode 100644 lib/vanila/application/repository/game_version_repository.dart create mode 100644 lib/vanila/application/use_case/get_game_version_list_use_case.dart create mode 100644 lib/vanila/domain/entity/game_version.dart create mode 100644 lib/vanila/framework/api/game_version_api_service_impl.dart create mode 100644 lib/vanila/framework/ui/game_version_dropdown.dart diff --git a/analysis_options.yaml b/analysis_options.yaml index 0d29021..a610cc8 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -22,7 +22,8 @@ linter: # producing the lint. rules: # avoid_print: false # Uncomment to disable the `avoid_print` rule - # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule + prefer_const_constructors: true # Additional information about this file can be found at # https://dart.dev/guides/language/analysis-options diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart new file mode 100644 index 0000000..eece9cf --- /dev/null +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -0,0 +1,19 @@ +import 'package:flutter/material.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/framework/ui/game_version_dropdown.dart'; + +class BasicConfigurationTab extends StatefulWidget { + const BasicConfigurationTab({super.key}); + + @override + State createState() => _BasicConfigurationTabState(); +} + +class _BasicConfigurationTabState extends State { + GameVersionViewModel? selectedGameVersion; + + @override + Widget build(BuildContext context) { + return const Column(children: [GameVersionDropdown(onChanged: print)]); + } +} diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 9435b8d..1e6f96f 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -1,10 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); @override Widget build(BuildContext context) { - return const Placeholder(); + return MaterialApp( + title: 'Minecraft Server Installer', + theme: ThemeData(primarySwatch: Colors.blue), + home: const Scaffold( + body: Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()), + ), + ); } -} \ No newline at end of file +} diff --git a/lib/main/framework/ui/strings.dart b/lib/main/framework/ui/strings.dart new file mode 100644 index 0000000..c7085f4 --- /dev/null +++ b/lib/main/framework/ui/strings.dart @@ -0,0 +1,3 @@ +abstract class Strings { + static const fieldGameVersion = '遊戲版本'; +} diff --git a/lib/vanila/adapter/gateway/game_version_api_service.dart b/lib/vanila/adapter/gateway/game_version_api_service.dart new file mode 100644 index 0000000..4fbe643 --- /dev/null +++ b/lib/vanila/adapter/gateway/game_version_api_service.dart @@ -0,0 +1,5 @@ +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +abstract interface class GameVersionApiService { + Future> fetchGameVersionList(); +} diff --git a/lib/vanila/adapter/gateway/game_version_repository_impl.dart b/lib/vanila/adapter/gateway/game_version_repository_impl.dart new file mode 100644 index 0000000..758105a --- /dev/null +++ b/lib/vanila/adapter/gateway/game_version_repository_impl.dart @@ -0,0 +1,12 @@ +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; +import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +class GameVersionRepositoryImpl implements GameVersionRepository { + final GameVersionApiService _gameVersionApiService; + + GameVersionRepositoryImpl(this._gameVersionApiService); + + @override + Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); +} diff --git a/lib/vanila/adapter/presentation/game_version_bloc.dart b/lib/vanila/adapter/presentation/game_version_bloc.dart new file mode 100644 index 0000000..f89019e --- /dev/null +++ b/lib/vanila/adapter/presentation/game_version_bloc.dart @@ -0,0 +1,22 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; + +class GameVersionBloc extends Bloc> { + final GetGameVersionListUseCase _getGameVersionListUseCase; + + GameVersionBloc(this._getGameVersionListUseCase) : super(const []) { + on((_, emit) async { + try { + final gameVersions = await _getGameVersionListUseCase(); + emit(gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList()); + } on Exception { + emit(const []); + } + }); + } +} + +sealed class GameVersionEvent {} + +class GameVersionLoadedEvent extends GameVersionEvent {} diff --git a/lib/vanila/adapter/presentation/game_version_view_model.dart b/lib/vanila/adapter/presentation/game_version_view_model.dart new file mode 100644 index 0000000..5f9607b --- /dev/null +++ b/lib/vanila/adapter/presentation/game_version_view_model.dart @@ -0,0 +1,14 @@ +import 'package:equatable/equatable.dart'; +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +class GameVersionViewModel with EquatableMixin { + final String name; + final Uri url; + + const GameVersionViewModel({required this.name, required this.url}); + + GameVersionViewModel.from(GameVersion gameVersion) : name = gameVersion.name, url = gameVersion.url; + + @override + List get props => [name, url]; +} diff --git a/lib/vanila/application/repository/game_version_repository.dart b/lib/vanila/application/repository/game_version_repository.dart new file mode 100644 index 0000000..836f45f --- /dev/null +++ b/lib/vanila/application/repository/game_version_repository.dart @@ -0,0 +1,5 @@ +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +abstract interface class GameVersionRepository { + Future> getGameVersionList(); +} diff --git a/lib/vanila/application/use_case/get_game_version_list_use_case.dart b/lib/vanila/application/use_case/get_game_version_list_use_case.dart new file mode 100644 index 0000000..355bdab --- /dev/null +++ b/lib/vanila/application/use_case/get_game_version_list_use_case.dart @@ -0,0 +1,10 @@ +import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +class GetGameVersionListUseCase { + final GameVersionRepository _gameVersionRepository; + + GetGameVersionListUseCase(this._gameVersionRepository); + + Future> call() => _gameVersionRepository.getGameVersionList(); +} diff --git a/lib/vanila/domain/entity/game_version.dart b/lib/vanila/domain/entity/game_version.dart new file mode 100644 index 0000000..f1c6321 --- /dev/null +++ b/lib/vanila/domain/entity/game_version.dart @@ -0,0 +1,11 @@ +import 'package:equatable/equatable.dart'; + +class GameVersion with EquatableMixin { + final String name; + final Uri url; + + const GameVersion({required this.name, required this.url}); + + @override + List get props => [name, url]; +} diff --git a/lib/vanila/framework/api/game_version_api_service_impl.dart b/lib/vanila/framework/api/game_version_api_service_impl.dart new file mode 100644 index 0000000..8762e79 --- /dev/null +++ b/lib/vanila/framework/api/game_version_api_service_impl.dart @@ -0,0 +1,20 @@ +import 'package:http/http.dart' as http; +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +class GameVersionApiServiceImpl implements GameVersionApiService { + @override + Future> fetchGameVersionList() async { + final sourceUrl = Uri.parse('https://www.dropbox.com/s/mtz3moc9dpjtz7s/GameVersions.txt?dl=1'); + final response = await http.get(sourceUrl); + + final rawGameVersionList = response.body.split('\n'); + final gameVersionList = + rawGameVersionList.map((line) => line.split(' ')).where((parts) => parts.length == 2).map((parts) { + final [name, url] = parts; + return GameVersion(name: name, url: Uri.parse(url)); + }).toList(); + + return gameVersionList; + } +} diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart new file mode 100644 index 0000000..48094b4 --- /dev/null +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -0,0 +1,64 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_repository_impl.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; +import 'package:minecraft_server_installer/vanila/framework/api/game_version_api_service_impl.dart'; + +class GameVersionDropdown extends StatelessWidget { + const GameVersionDropdown({super.key, required this.onChanged}); + + final void Function(GameVersionViewModel?) onChanged; + + @override + Widget build(BuildContext context) { + return BlocProvider( + create: (_) { + final gameVersionApiService = GameVersionApiServiceImpl(); + final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService); + final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); + return GameVersionBloc(getGameVersionListUseCase); + }, + child: _GameVersionDropdown(key: key, onChanged: onChanged), + ); + } +} + +class _GameVersionDropdown extends StatefulWidget { + const _GameVersionDropdown({super.key, required this.onChanged}); + + final void Function(GameVersionViewModel?) onChanged; + + @override + State<_GameVersionDropdown> createState() => _GameVersionDropdownState(); +} + +class _GameVersionDropdownState extends State<_GameVersionDropdown> { + @override + void initState() { + super.initState(); + context.read().add(GameVersionLoadedEvent()); + } + + @override + Widget build(BuildContext context) => BlocConsumer>( + listener: (_, __) {}, + builder: + (_, gameVersions) => DropdownMenu( + enabled: gameVersions.isNotEmpty, + requestFocusOnTap: false, + expandedInsets: EdgeInsets.zero, + label: const Text(Strings.fieldGameVersion), + onSelected: widget.onChanged, + dropdownMenuEntries: + gameVersions + .map( + (gameVersion) => + DropdownMenuEntry(value: gameVersion, label: gameVersion.name), + ) + .toList(), + ), + ); +} diff --git a/pubspec.lock b/pubspec.lock index d993b91..f516c4f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.12.0" + bloc: + dependency: transitive + description: + name: bloc + sha256: "52c10575f4445c61dd9e0cafcc6356fdd827c4c64dd7945ef3c4105f6b6ac189" + url: "https://pub.dev" + source: hosted + version: "9.0.0" boolean_selector: dependency: transitive description: @@ -49,6 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.8" + equatable: + dependency: "direct main" + description: + name: equatable + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" + url: "https://pub.dev" + source: hosted + version: "2.0.7" fake_async: dependency: transitive description: @@ -62,6 +78,14 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_bloc: + dependency: "direct main" + description: + name: flutter_bloc + sha256: cf51747952201a455a1c840f8171d273be009b932c75093020f9af64f2123e38 + url: "https://pub.dev" + source: hosted + version: "9.1.1" flutter_lints: dependency: "direct dev" description: @@ -75,6 +99,22 @@ packages: description: flutter source: sdk version: "0.0.0" + http: + dependency: "direct main" + description: + name: http + sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b" + url: "https://pub.dev" + source: hosted + version: "1.4.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" leak_tracker: dependency: transitive description: @@ -131,6 +171,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.16.0" + nested: + dependency: transitive + description: + name: nested + sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" + url: "https://pub.dev" + source: hosted + version: "1.0.0" path: dependency: transitive description: @@ -139,6 +187,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + provider: + dependency: transitive + description: + name: provider + sha256: "4abbd070a04e9ddc287673bf5a030c7ca8b685ff70218720abab8b092f53dd84" + url: "https://pub.dev" + source: hosted + version: "6.1.5" sky_engine: dependency: transitive description: flutter @@ -192,6 +248,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.4" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -208,6 +272,14 @@ packages: url: "https://pub.dev" source: hosted version: "14.3.1" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" sdks: dart: ">=3.7.2 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index fcd7c78..348e62e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -34,6 +34,9 @@ dependencies: # The following adds the Cupertino Icons font to your application. # Use with the CupertinoIcons class for iOS style icons. cupertino_icons: ^1.0.8 + equatable: ^2.0.7 + flutter_bloc: ^9.1.1 + http: ^1.4.0 dev_dependencies: flutter_test: From 91298fd13e5c54c42979313fbb08cf06db39152b Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 11:58:45 +0800 Subject: [PATCH 05/12] MCSI-1 refactor: move bloc higher layer --- .../ui/minecraft_server_installer.dart | 16 +++++++++-- .../framework/ui/game_version_dropdown.dart | 28 ++----------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 1e6f96f..82957ff 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -1,16 +1,28 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_repository_impl.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; +import 'package:minecraft_server_installer/vanila/framework/api/game_version_api_service_impl.dart'; class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); @override Widget build(BuildContext context) { + final gameVersionApiService = GameVersionApiServiceImpl(); + final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService); + final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); + return MaterialApp( title: 'Minecraft Server Installer', theme: ThemeData(primarySwatch: Colors.blue), - home: const Scaffold( - body: Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()), + home: MultiBlocProvider( + providers: [BlocProvider(create: (context) => GameVersionBloc(getGameVersionListUseCase))], + child: const Scaffold( + body: Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()), + ), ), ); } diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart index 48094b4..f3128fa 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -1,41 +1,19 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_repository_impl.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -import 'package:minecraft_server_installer/vanila/framework/api/game_version_api_service_impl.dart'; -class GameVersionDropdown extends StatelessWidget { +class GameVersionDropdown extends StatefulWidget { const GameVersionDropdown({super.key, required this.onChanged}); final void Function(GameVersionViewModel?) onChanged; @override - Widget build(BuildContext context) { - return BlocProvider( - create: (_) { - final gameVersionApiService = GameVersionApiServiceImpl(); - final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService); - final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); - return GameVersionBloc(getGameVersionListUseCase); - }, - child: _GameVersionDropdown(key: key, onChanged: onChanged), - ); - } + State createState() => _GameVersionDropdownState(); } -class _GameVersionDropdown extends StatefulWidget { - const _GameVersionDropdown({super.key, required this.onChanged}); - - final void Function(GameVersionViewModel?) onChanged; - - @override - State<_GameVersionDropdown> createState() => _GameVersionDropdownState(); -} - -class _GameVersionDropdownState extends State<_GameVersionDropdown> { +class _GameVersionDropdownState extends State { @override void initState() { super.initState(); From d0407512622a070998b343f609bd7e1b51a89be2 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 14:58:29 +0900 Subject: [PATCH 06/12] MCSI-1 feat: download server file --- .vscode/settings.json | 1 + .../framework/ui/basic_configuration_tab.dart | 19 +++++- .../ui/minecraft_server_installer.dart | 12 +++- lib/main/framework/ui/strings.dart | 1 + .../gateway/game_version_api_service.dart | 4 ++ .../gateway/game_version_file_storage.dart | 5 ++ .../gateway/game_version_repository_impl.dart | 10 ++- .../presentation/game_version_bloc.dart | 63 ++++++++++++++++--- .../presentation/game_version_view_model.dart | 4 ++ .../repository/game_version_repository.dart | 2 + .../download_server_file_use_case.dart | 10 +++ .../api/game_version_api_service_impl.dart | 8 +++ .../game_version_file_storage_impl.dart | 18 ++++++ .../framework/ui/game_version_dropdown.dart | 20 +++--- 14 files changed, 157 insertions(+), 20 deletions(-) create mode 100644 lib/vanila/adapter/gateway/game_version_file_storage.dart create mode 100644 lib/vanila/application/use_case/download_server_file_use_case.dart create mode 100644 lib/vanila/framework/storage/game_version_file_storage_impl.dart diff --git a/.vscode/settings.json b/.vscode/settings.json index f4607f2..146b1f5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,4 @@ { + "dart.flutterSdkPath": ".fvm/versions/3.29.3", "dart.lineLength": 120 } diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index eece9cf..24b4d15 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -1,4 +1,7 @@ import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; import 'package:minecraft_server_installer/vanila/framework/ui/game_version_dropdown.dart'; @@ -14,6 +17,20 @@ class _BasicConfigurationTabState extends State { @override Widget build(BuildContext context) { - return const Column(children: [GameVersionDropdown(onChanged: print)]); + return Column( + children: [ + const GameVersionDropdown(), + const Spacer(), + ElevatedButton.icon( + onPressed: context.watch().state.isGameVersionSelected ? _downloadServerFile : null, + icon: const Icon(Icons.download), + label: const Text(Strings.buttonStartToInstall), + ), + ], + ); + } + + void _downloadServerFile() { + context.read().add(VanilaServerFileDownloadedEvent()); } } diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 82957ff..55bb6ce 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -3,8 +3,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_repository_impl.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; import 'package:minecraft_server_installer/vanila/framework/api/game_version_api_service_impl.dart'; +import 'package:minecraft_server_installer/vanila/framework/storage/game_version_file_storage_impl.dart'; class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); @@ -12,14 +14,20 @@ class MinecraftServerInstaller extends StatelessWidget { @override Widget build(BuildContext context) { final gameVersionApiService = GameVersionApiServiceImpl(); - final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService); + final gameVersionFileStorage = GameVersionFileStorageImpl(); + final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService, gameVersionFileStorage); final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); + final downloadServerFileUseCase = DownloadServerFileUseCase(gameVersionRepository); return MaterialApp( title: 'Minecraft Server Installer', theme: ThemeData(primarySwatch: Colors.blue), home: MultiBlocProvider( - providers: [BlocProvider(create: (context) => GameVersionBloc(getGameVersionListUseCase))], + providers: [ + BlocProvider( + create: (context) => GameVersionBloc(getGameVersionListUseCase, downloadServerFileUseCase), + ), + ], child: const Scaffold( body: Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()), ), diff --git a/lib/main/framework/ui/strings.dart b/lib/main/framework/ui/strings.dart index c7085f4..25acf60 100644 --- a/lib/main/framework/ui/strings.dart +++ b/lib/main/framework/ui/strings.dart @@ -1,3 +1,4 @@ abstract class Strings { static const fieldGameVersion = '遊戲版本'; + static const buttonStartToInstall = '開始安裝'; } diff --git a/lib/vanila/adapter/gateway/game_version_api_service.dart b/lib/vanila/adapter/gateway/game_version_api_service.dart index 4fbe643..9e3dc1c 100644 --- a/lib/vanila/adapter/gateway/game_version_api_service.dart +++ b/lib/vanila/adapter/gateway/game_version_api_service.dart @@ -1,5 +1,9 @@ +import 'dart:typed_data'; + import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; abstract interface class GameVersionApiService { Future> fetchGameVersionList(); + + Future fetchServerFile(Uri url); } diff --git a/lib/vanila/adapter/gateway/game_version_file_storage.dart b/lib/vanila/adapter/gateway/game_version_file_storage.dart new file mode 100644 index 0000000..d4eac33 --- /dev/null +++ b/lib/vanila/adapter/gateway/game_version_file_storage.dart @@ -0,0 +1,5 @@ +import 'dart:typed_data'; + +abstract interface class GameVersionFileStorage { + Future saveFile(Uint8List fileBytes, String savePath); +} diff --git a/lib/vanila/adapter/gateway/game_version_repository_impl.dart b/lib/vanila/adapter/gateway/game_version_repository_impl.dart index 758105a..12bb1a2 100644 --- a/lib/vanila/adapter/gateway/game_version_repository_impl.dart +++ b/lib/vanila/adapter/gateway/game_version_repository_impl.dart @@ -1,12 +1,20 @@ import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_file_storage.dart'; import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; class GameVersionRepositoryImpl implements GameVersionRepository { final GameVersionApiService _gameVersionApiService; + final GameVersionFileStorage _gameVersionFileStorage; - GameVersionRepositoryImpl(this._gameVersionApiService); + GameVersionRepositoryImpl(this._gameVersionApiService, this._gameVersionFileStorage); @override Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); + + @override + Future downloadServerFile(GameVersion version, String savePath) async { + final fileBytes = await _gameVersionApiService.fetchServerFile(version.url); + await _gameVersionFileStorage.saveFile(fileBytes, savePath); + } } diff --git a/lib/vanila/adapter/presentation/game_version_bloc.dart b/lib/vanila/adapter/presentation/game_version_bloc.dart index f89019e..597f957 100644 --- a/lib/vanila/adapter/presentation/game_version_bloc.dart +++ b/lib/vanila/adapter/presentation/game_version_bloc.dart @@ -1,22 +1,71 @@ +import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -class GameVersionBloc extends Bloc> { +class GameVersionBloc extends Bloc { final GetGameVersionListUseCase _getGameVersionListUseCase; + final DownloadServerFileUseCase _downloadServerFileUseCase; - GameVersionBloc(this._getGameVersionListUseCase) : super(const []) { - on((_, emit) async { + GameVersionBloc(this._getGameVersionListUseCase, this._downloadServerFileUseCase) : super(const VanilaState.empty()) { + on((_, emit) async { try { final gameVersions = await _getGameVersionListUseCase(); - emit(gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList()); + emit( + VanilaState( + gameVersions: gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList(), + selectedGameVersion: null, + ), + ); } on Exception { - emit(const []); + emit(const VanilaState.empty()); } }); + + on((event, emit) { + emit(state.copyWith(selectedGameVersion: event.gameVersion)); + }); + + on((_, emit) async { + final gameVersion = state.selectedGameVersion; + if (gameVersion == null) { + return; + } + + await _downloadServerFileUseCase(gameVersion.toEntity(), './server.jar'); + }); } } -sealed class GameVersionEvent {} +sealed class VanilaEvent {} -class GameVersionLoadedEvent extends GameVersionEvent {} +class VanilaGameVersionListLoadedEvent extends VanilaEvent {} + +class VanilaGameVersionSelectedEvent extends VanilaEvent { + final GameVersionViewModel gameVersion; + + VanilaGameVersionSelectedEvent(this.gameVersion); +} + +class VanilaServerFileDownloadedEvent extends VanilaEvent {} + +class VanilaState with EquatableMixin { + final List gameVersions; + final GameVersionViewModel? selectedGameVersion; + + const VanilaState({required this.gameVersions, required this.selectedGameVersion}); + + const VanilaState.empty() : this(gameVersions: const [], selectedGameVersion: null); + + @override + List get props => [gameVersions, selectedGameVersion]; + + bool get isGameVersionSelected => selectedGameVersion != null; + + VanilaState copyWith({List? gameVersions, GameVersionViewModel? selectedGameVersion}) => + VanilaState( + gameVersions: gameVersions ?? this.gameVersions, + selectedGameVersion: selectedGameVersion ?? this.selectedGameVersion, + ); +} diff --git a/lib/vanila/adapter/presentation/game_version_view_model.dart b/lib/vanila/adapter/presentation/game_version_view_model.dart index 5f9607b..de8a870 100644 --- a/lib/vanila/adapter/presentation/game_version_view_model.dart +++ b/lib/vanila/adapter/presentation/game_version_view_model.dart @@ -9,6 +9,10 @@ class GameVersionViewModel with EquatableMixin { GameVersionViewModel.from(GameVersion gameVersion) : name = gameVersion.name, url = gameVersion.url; + GameVersion toEntity() { + return GameVersion(name: name, url: url); + } + @override List get props => [name, url]; } diff --git a/lib/vanila/application/repository/game_version_repository.dart b/lib/vanila/application/repository/game_version_repository.dart index 836f45f..bf9d2d5 100644 --- a/lib/vanila/application/repository/game_version_repository.dart +++ b/lib/vanila/application/repository/game_version_repository.dart @@ -2,4 +2,6 @@ import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dar abstract interface class GameVersionRepository { Future> getGameVersionList(); + + Future downloadServerFile(GameVersion version, String savePath); } diff --git a/lib/vanila/application/use_case/download_server_file_use_case.dart b/lib/vanila/application/use_case/download_server_file_use_case.dart new file mode 100644 index 0000000..02b5a34 --- /dev/null +++ b/lib/vanila/application/use_case/download_server_file_use_case.dart @@ -0,0 +1,10 @@ +import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; + +class DownloadServerFileUseCase { + final GameVersionRepository _gameVersionRepository; + + DownloadServerFileUseCase(this._gameVersionRepository); + + Future call(GameVersion version, String savePath) => _gameVersionRepository.downloadServerFile(version, savePath); +} diff --git a/lib/vanila/framework/api/game_version_api_service_impl.dart b/lib/vanila/framework/api/game_version_api_service_impl.dart index 8762e79..5cfc416 100644 --- a/lib/vanila/framework/api/game_version_api_service_impl.dart +++ b/lib/vanila/framework/api/game_version_api_service_impl.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:http/http.dart' as http; import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; @@ -17,4 +19,10 @@ class GameVersionApiServiceImpl implements GameVersionApiService { return gameVersionList; } + + @override + Future fetchServerFile(Uri url) async { + final response = await http.get(url); + return response.bodyBytes; + } } diff --git a/lib/vanila/framework/storage/game_version_file_storage_impl.dart b/lib/vanila/framework/storage/game_version_file_storage_impl.dart new file mode 100644 index 0000000..73c116a --- /dev/null +++ b/lib/vanila/framework/storage/game_version_file_storage_impl.dart @@ -0,0 +1,18 @@ +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_file_storage.dart'; + +class GameVersionFileStorageImpl implements GameVersionFileStorage { + @override + Future saveFile(Uint8List fileBytes, String savePath) async { + final file = File(savePath); + + if (!await file.parent.exists()) { + await file.parent.create(recursive: true); + } + + await file.create(); + await file.writeAsBytes(fileBytes, flush: true); + } +} diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart index f3128fa..4ab1c5f 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -5,9 +5,7 @@ import 'package:minecraft_server_installer/vanila/adapter/presentation/game_vers import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; class GameVersionDropdown extends StatefulWidget { - const GameVersionDropdown({super.key, required this.onChanged}); - - final void Function(GameVersionViewModel?) onChanged; + const GameVersionDropdown({super.key}); @override State createState() => _GameVersionDropdownState(); @@ -17,21 +15,25 @@ class _GameVersionDropdownState extends State { @override void initState() { super.initState(); - context.read().add(GameVersionLoadedEvent()); + context.read().add(VanilaGameVersionListLoadedEvent()); } @override - Widget build(BuildContext context) => BlocConsumer>( + Widget build(BuildContext context) => BlocConsumer( listener: (_, __) {}, builder: - (_, gameVersions) => DropdownMenu( - enabled: gameVersions.isNotEmpty, + (_, state) => DropdownMenu( + enabled: state.gameVersions.isNotEmpty, requestFocusOnTap: false, expandedInsets: EdgeInsets.zero, label: const Text(Strings.fieldGameVersion), - onSelected: widget.onChanged, + onSelected: (value) { + if (value != null) { + context.read().add(VanilaGameVersionSelectedEvent(value)); + } + }, dropdownMenuEntries: - gameVersions + state.gameVersions .map( (gameVersion) => DropdownMenuEntry(value: gameVersion, label: gameVersion.name), From ebd764ab58f90364954f0b426547ba0f8f3b7afc Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 15:04:35 +0900 Subject: [PATCH 07/12] MCSI-1 refactor: rename game version -> vanila --- .../framework/ui/basic_configuration_tab.dart | 6 ++--- .../ui/minecraft_server_installer.dart | 18 ++++++------- ...i_service.dart => vanila_api_service.dart} | 2 +- ..._storage.dart => vanila_file_storage.dart} | 2 +- ..._impl.dart => vanila_repository_impl.dart} | 14 +++++----- ...ame_version_bloc.dart => vanila_bloc.dart} | 26 +++---------------- .../adapter/presentation/vanila_state.dart | 23 ++++++++++++++++ ...repository.dart => vanila_repository.dart} | 2 +- .../download_server_file_use_case.dart | 4 +-- .../get_game_version_list_use_case.dart | 4 +-- ...impl.dart => vanila_api_service_impl.dart} | 4 +-- ...mpl.dart => vanila_file_storage_impl.dart} | 4 +-- .../framework/ui/game_version_dropdown.dart | 9 ++++--- 13 files changed, 61 insertions(+), 57 deletions(-) rename lib/vanila/adapter/gateway/{game_version_api_service.dart => vanila_api_service.dart} (81%) rename lib/vanila/adapter/gateway/{game_version_file_storage.dart => vanila_file_storage.dart} (64%) rename lib/vanila/adapter/gateway/{game_version_repository_impl.dart => vanila_repository_impl.dart} (62%) rename lib/vanila/adapter/presentation/{game_version_bloc.dart => vanila_bloc.dart} (62%) create mode 100644 lib/vanila/adapter/presentation/vanila_state.dart rename lib/vanila/application/repository/{game_version_repository.dart => vanila_repository.dart} (81%) rename lib/vanila/framework/api/{game_version_api_service_impl.dart => vanila_api_service_impl.dart} (90%) rename lib/vanila/framework/storage/{game_version_file_storage_impl.dart => vanila_file_storage_impl.dart} (79%) diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index 24b4d15..ca145a0 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; import 'package:minecraft_server_installer/vanila/framework/ui/game_version_dropdown.dart'; @@ -22,7 +22,7 @@ class _BasicConfigurationTabState extends State { const GameVersionDropdown(), const Spacer(), ElevatedButton.icon( - onPressed: context.watch().state.isGameVersionSelected ? _downloadServerFile : null, + onPressed: context.watch().state.isGameVersionSelected ? _downloadServerFile : null, icon: const Icon(Icons.download), label: const Text(Strings.buttonStartToInstall), ), @@ -31,6 +31,6 @@ class _BasicConfigurationTabState extends State { } void _downloadServerFile() { - context.read().add(VanilaServerFileDownloadedEvent()); + context.read().add(VanilaServerFileDownloadedEvent()); } } diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 55bb6ce..5913c58 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -1,21 +1,21 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_repository_impl.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_repository_impl.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -import 'package:minecraft_server_installer/vanila/framework/api/game_version_api_service_impl.dart'; -import 'package:minecraft_server_installer/vanila/framework/storage/game_version_file_storage_impl.dart'; +import 'package:minecraft_server_installer/vanila/framework/api/vanila_api_service_impl.dart'; +import 'package:minecraft_server_installer/vanila/framework/storage/vanila_file_storage_impl.dart'; class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); @override Widget build(BuildContext context) { - final gameVersionApiService = GameVersionApiServiceImpl(); - final gameVersionFileStorage = GameVersionFileStorageImpl(); - final gameVersionRepository = GameVersionRepositoryImpl(gameVersionApiService, gameVersionFileStorage); + final gameVersionApiService = VanilaApiServiceImpl(); + final gameVersionFileStorage = VanilaFileStorageImpl(); + final gameVersionRepository = VanilaRepositoryImpl(gameVersionApiService, gameVersionFileStorage); final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); final downloadServerFileUseCase = DownloadServerFileUseCase(gameVersionRepository); @@ -24,8 +24,8 @@ class MinecraftServerInstaller extends StatelessWidget { theme: ThemeData(primarySwatch: Colors.blue), home: MultiBlocProvider( providers: [ - BlocProvider( - create: (context) => GameVersionBloc(getGameVersionListUseCase, downloadServerFileUseCase), + BlocProvider( + create: (context) => VanilaBloc(getGameVersionListUseCase, downloadServerFileUseCase), ), ], child: const Scaffold( diff --git a/lib/vanila/adapter/gateway/game_version_api_service.dart b/lib/vanila/adapter/gateway/vanila_api_service.dart similarity index 81% rename from lib/vanila/adapter/gateway/game_version_api_service.dart rename to lib/vanila/adapter/gateway/vanila_api_service.dart index 9e3dc1c..a3fa6fb 100644 --- a/lib/vanila/adapter/gateway/game_version_api_service.dart +++ b/lib/vanila/adapter/gateway/vanila_api_service.dart @@ -2,7 +2,7 @@ import 'dart:typed_data'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; -abstract interface class GameVersionApiService { +abstract interface class VanilaApiService { Future> fetchGameVersionList(); Future fetchServerFile(Uri url); diff --git a/lib/vanila/adapter/gateway/game_version_file_storage.dart b/lib/vanila/adapter/gateway/vanila_file_storage.dart similarity index 64% rename from lib/vanila/adapter/gateway/game_version_file_storage.dart rename to lib/vanila/adapter/gateway/vanila_file_storage.dart index d4eac33..7ba203d 100644 --- a/lib/vanila/adapter/gateway/game_version_file_storage.dart +++ b/lib/vanila/adapter/gateway/vanila_file_storage.dart @@ -1,5 +1,5 @@ import 'dart:typed_data'; -abstract interface class GameVersionFileStorage { +abstract interface class VanilaFileStorage { Future saveFile(Uint8List fileBytes, String savePath); } diff --git a/lib/vanila/adapter/gateway/game_version_repository_impl.dart b/lib/vanila/adapter/gateway/vanila_repository_impl.dart similarity index 62% rename from lib/vanila/adapter/gateway/game_version_repository_impl.dart rename to lib/vanila/adapter/gateway/vanila_repository_impl.dart index 12bb1a2..f60e44e 100644 --- a/lib/vanila/adapter/gateway/game_version_repository_impl.dart +++ b/lib/vanila/adapter/gateway/vanila_repository_impl.dart @@ -1,13 +1,13 @@ -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_file_storage.dart'; -import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_file_storage.dart'; +import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; -class GameVersionRepositoryImpl implements GameVersionRepository { - final GameVersionApiService _gameVersionApiService; - final GameVersionFileStorage _gameVersionFileStorage; +class VanilaRepositoryImpl implements VanilaRepository { + final VanilaApiService _gameVersionApiService; + final VanilaFileStorage _gameVersionFileStorage; - GameVersionRepositoryImpl(this._gameVersionApiService, this._gameVersionFileStorage); + VanilaRepositoryImpl(this._gameVersionApiService, this._gameVersionFileStorage); @override Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); diff --git a/lib/vanila/adapter/presentation/game_version_bloc.dart b/lib/vanila/adapter/presentation/vanila_bloc.dart similarity index 62% rename from lib/vanila/adapter/presentation/game_version_bloc.dart rename to lib/vanila/adapter/presentation/vanila_bloc.dart index 597f957..e5d1188 100644 --- a/lib/vanila/adapter/presentation/game_version_bloc.dart +++ b/lib/vanila/adapter/presentation/vanila_bloc.dart @@ -1,14 +1,14 @@ -import 'package:equatable/equatable.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -class GameVersionBloc extends Bloc { +class VanilaBloc extends Bloc { final GetGameVersionListUseCase _getGameVersionListUseCase; final DownloadServerFileUseCase _downloadServerFileUseCase; - GameVersionBloc(this._getGameVersionListUseCase, this._downloadServerFileUseCase) : super(const VanilaState.empty()) { + VanilaBloc(this._getGameVersionListUseCase, this._downloadServerFileUseCase) : super(const VanilaState.empty()) { on((_, emit) async { try { final gameVersions = await _getGameVersionListUseCase(); @@ -49,23 +49,3 @@ class VanilaGameVersionSelectedEvent extends VanilaEvent { } class VanilaServerFileDownloadedEvent extends VanilaEvent {} - -class VanilaState with EquatableMixin { - final List gameVersions; - final GameVersionViewModel? selectedGameVersion; - - const VanilaState({required this.gameVersions, required this.selectedGameVersion}); - - const VanilaState.empty() : this(gameVersions: const [], selectedGameVersion: null); - - @override - List get props => [gameVersions, selectedGameVersion]; - - bool get isGameVersionSelected => selectedGameVersion != null; - - VanilaState copyWith({List? gameVersions, GameVersionViewModel? selectedGameVersion}) => - VanilaState( - gameVersions: gameVersions ?? this.gameVersions, - selectedGameVersion: selectedGameVersion ?? this.selectedGameVersion, - ); -} diff --git a/lib/vanila/adapter/presentation/vanila_state.dart b/lib/vanila/adapter/presentation/vanila_state.dart new file mode 100644 index 0000000..ee1ca90 --- /dev/null +++ b/lib/vanila/adapter/presentation/vanila_state.dart @@ -0,0 +1,23 @@ + +import 'package:equatable/equatable.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; + +class VanilaState with EquatableMixin { + final List gameVersions; + final GameVersionViewModel? selectedGameVersion; + + const VanilaState({required this.gameVersions, required this.selectedGameVersion}); + + const VanilaState.empty() : this(gameVersions: const [], selectedGameVersion: null); + + @override + List get props => [gameVersions, selectedGameVersion]; + + bool get isGameVersionSelected => selectedGameVersion != null; + + VanilaState copyWith({List? gameVersions, GameVersionViewModel? selectedGameVersion}) => + VanilaState( + gameVersions: gameVersions ?? this.gameVersions, + selectedGameVersion: selectedGameVersion ?? this.selectedGameVersion, + ); +} \ No newline at end of file diff --git a/lib/vanila/application/repository/game_version_repository.dart b/lib/vanila/application/repository/vanila_repository.dart similarity index 81% rename from lib/vanila/application/repository/game_version_repository.dart rename to lib/vanila/application/repository/vanila_repository.dart index bf9d2d5..faa8b62 100644 --- a/lib/vanila/application/repository/game_version_repository.dart +++ b/lib/vanila/application/repository/vanila_repository.dart @@ -1,6 +1,6 @@ import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; -abstract interface class GameVersionRepository { +abstract interface class VanilaRepository { Future> getGameVersionList(); Future downloadServerFile(GameVersion version, String savePath); diff --git a/lib/vanila/application/use_case/download_server_file_use_case.dart b/lib/vanila/application/use_case/download_server_file_use_case.dart index 02b5a34..e01d2a8 100644 --- a/lib/vanila/application/use_case/download_server_file_use_case.dart +++ b/lib/vanila/application/use_case/download_server_file_use_case.dart @@ -1,8 +1,8 @@ -import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; class DownloadServerFileUseCase { - final GameVersionRepository _gameVersionRepository; + final VanilaRepository _gameVersionRepository; DownloadServerFileUseCase(this._gameVersionRepository); diff --git a/lib/vanila/application/use_case/get_game_version_list_use_case.dart b/lib/vanila/application/use_case/get_game_version_list_use_case.dart index 355bdab..b99349f 100644 --- a/lib/vanila/application/use_case/get_game_version_list_use_case.dart +++ b/lib/vanila/application/use_case/get_game_version_list_use_case.dart @@ -1,8 +1,8 @@ -import 'package:minecraft_server_installer/vanila/application/repository/game_version_repository.dart'; +import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; class GetGameVersionListUseCase { - final GameVersionRepository _gameVersionRepository; + final VanilaRepository _gameVersionRepository; GetGameVersionListUseCase(this._gameVersionRepository); diff --git a/lib/vanila/framework/api/game_version_api_service_impl.dart b/lib/vanila/framework/api/vanila_api_service_impl.dart similarity index 90% rename from lib/vanila/framework/api/game_version_api_service_impl.dart rename to lib/vanila/framework/api/vanila_api_service_impl.dart index 5cfc416..19fd8c1 100644 --- a/lib/vanila/framework/api/game_version_api_service_impl.dart +++ b/lib/vanila/framework/api/vanila_api_service_impl.dart @@ -1,10 +1,10 @@ import 'dart:typed_data'; import 'package:http/http.dart' as http; -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_api_service.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; -class GameVersionApiServiceImpl implements GameVersionApiService { +class VanilaApiServiceImpl implements VanilaApiService { @override Future> fetchGameVersionList() async { final sourceUrl = Uri.parse('https://www.dropbox.com/s/mtz3moc9dpjtz7s/GameVersions.txt?dl=1'); diff --git a/lib/vanila/framework/storage/game_version_file_storage_impl.dart b/lib/vanila/framework/storage/vanila_file_storage_impl.dart similarity index 79% rename from lib/vanila/framework/storage/game_version_file_storage_impl.dart rename to lib/vanila/framework/storage/vanila_file_storage_impl.dart index 73c116a..656379e 100644 --- a/lib/vanila/framework/storage/game_version_file_storage_impl.dart +++ b/lib/vanila/framework/storage/vanila_file_storage_impl.dart @@ -1,9 +1,9 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/game_version_file_storage.dart'; +import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_file_storage.dart'; -class GameVersionFileStorageImpl implements GameVersionFileStorage { +class VanilaFileStorageImpl implements VanilaFileStorage { @override Future saveFile(Uint8List fileBytes, String savePath) async { final file = File(savePath); diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart index 4ab1c5f..7f5b8af 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; class GameVersionDropdown extends StatefulWidget { const GameVersionDropdown({super.key}); @@ -15,11 +16,11 @@ class _GameVersionDropdownState extends State { @override void initState() { super.initState(); - context.read().add(VanilaGameVersionListLoadedEvent()); + context.read().add(VanilaGameVersionListLoadedEvent()); } @override - Widget build(BuildContext context) => BlocConsumer( + Widget build(BuildContext context) => BlocConsumer( listener: (_, __) {}, builder: (_, state) => DropdownMenu( @@ -29,7 +30,7 @@ class _GameVersionDropdownState extends State { label: const Text(Strings.fieldGameVersion), onSelected: (value) { if (value != null) { - context.read().add(VanilaGameVersionSelectedEvent(value)); + context.read().add(VanilaGameVersionSelectedEvent(value)); } }, dropdownMenuEntries: From 95bf2bc86ec3b542bd2c51190413ce0321bdbdd9 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 15:10:29 +0900 Subject: [PATCH 08/12] MCSI-1 refactor: extract 'server.jar' --- lib/main/constants.dart | 3 +++ lib/vanila/adapter/presentation/vanila_bloc.dart | 4 +++- pubspec.lock | 2 +- pubspec.yaml | 1 + 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 lib/main/constants.dart diff --git a/lib/main/constants.dart b/lib/main/constants.dart new file mode 100644 index 0000000..34de687 --- /dev/null +++ b/lib/main/constants.dart @@ -0,0 +1,3 @@ +abstract class Constants { + static const serverFileName = 'server.jar'; +} diff --git a/lib/vanila/adapter/presentation/vanila_bloc.dart b/lib/vanila/adapter/presentation/vanila_bloc.dart index e5d1188..65be91d 100644 --- a/lib/vanila/adapter/presentation/vanila_bloc.dart +++ b/lib/vanila/adapter/presentation/vanila_bloc.dart @@ -1,8 +1,10 @@ import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/main/constants.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; +import 'package:path/path.dart' as path; class VanilaBloc extends Bloc { final GetGameVersionListUseCase _getGameVersionListUseCase; @@ -33,7 +35,7 @@ class VanilaBloc extends Bloc { return; } - await _downloadServerFileUseCase(gameVersion.toEntity(), './server.jar'); + await _downloadServerFileUseCase(gameVersion.toEntity(), path.join('.', Constants.serverFileName)); }); } } diff --git a/pubspec.lock b/pubspec.lock index f516c4f..fe23d2a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -180,7 +180,7 @@ packages: source: hosted version: "1.0.0" path: - dependency: transitive + dependency: "direct main" description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" diff --git a/pubspec.yaml b/pubspec.yaml index 348e62e..7c90d05 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: equatable: ^2.0.7 flutter_bloc: ^9.1.1 http: ^1.4.0 + path: ^1.9.1 dev_dependencies: flutter_test: From f1716b05050bb3b3252ff93b74765d6540df98e2 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 17:23:14 +0900 Subject: [PATCH 09/12] MCSI-1 feat: download progress --- lib/main/constants.dart | 1 + .../framework/ui/basic_configuration_tab.dart | 27 ++++++++++++-- .../ui/minecraft_server_installer.dart | 24 ++++++++++-- .../adapter/gateway/vanila_api_service.dart | 3 +- .../gateway/vanila_repository_impl.dart | 9 ++++- .../adapter/presentation/vanila_bloc.dart | 27 ++++++++++++-- .../adapter/presentation/vanila_state.dart | 35 +++++++++++++----- .../repository/vanila_repository.dart | 3 +- .../download_server_file_use_case.dart | 5 ++- .../api/vanila_api_service_impl.dart | 37 +++++++++++++++++-- .../framework/ui/game_version_dropdown.dart | 13 +------ pubspec.lock | 8 ++++ pubspec.yaml | 4 +- 13 files changed, 152 insertions(+), 44 deletions(-) diff --git a/lib/main/constants.dart b/lib/main/constants.dart index 34de687..1226125 100644 --- a/lib/main/constants.dart +++ b/lib/main/constants.dart @@ -1,3 +1,4 @@ abstract class Constants { + static const gameVersionListUrl = 'https://www.dropbox.com/s/mtz3moc9dpjtz7s/GameVersions.txt?dl=1'; static const serverFileName = 'server.jar'; } diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index ca145a0..c29173b 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -1,8 +1,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:gap/gap.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; import 'package:minecraft_server_installer/vanila/framework/ui/game_version_dropdown.dart'; class BasicConfigurationTab extends StatefulWidget { @@ -21,10 +23,27 @@ class _BasicConfigurationTabState extends State { children: [ const GameVersionDropdown(), const Spacer(), - ElevatedButton.icon( - onPressed: context.watch().state.isGameVersionSelected ? _downloadServerFile : null, - icon: const Icon(Icons.download), - label: const Text(Strings.buttonStartToInstall), + BlocConsumer( + listener: (_, __) {}, + builder: + (context, state) => Row( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if (state.isDownloading) Expanded(child: LinearProgressIndicator(value: state.downloadProgress)), + const Gap(32), + ElevatedButton.icon( + style: ElevatedButton.styleFrom( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + ), + onPressed: state.isGameVersionSelected ? _downloadServerFile : null, + icon: const Icon(Icons.download), + label: const Padding( + padding: EdgeInsets.symmetric(vertical: 12), + child: Text(Strings.buttonStartToInstall), + ), + ), + ], + ), ), ], ); diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 5913c58..3151b1b 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -3,6 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_repository_impl.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; +import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; import 'package:minecraft_server_installer/vanila/framework/api/vanila_api_service_impl.dart'; @@ -11,6 +12,9 @@ import 'package:minecraft_server_installer/vanila/framework/storage/vanila_file_ class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); + Widget get _body => + const Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()); + @override Widget build(BuildContext context) { final gameVersionApiService = VanilaApiServiceImpl(); @@ -21,15 +25,27 @@ class MinecraftServerInstaller extends StatelessWidget { return MaterialApp( title: 'Minecraft Server Installer', - theme: ThemeData(primarySwatch: Colors.blue), + theme: ThemeData.light().copyWith(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade900)), home: MultiBlocProvider( providers: [ BlocProvider( - create: (context) => VanilaBloc(getGameVersionListUseCase, downloadServerFileUseCase), + create: + (context) => + VanilaBloc(getGameVersionListUseCase, downloadServerFileUseCase) + ..add(VanilaGameVersionListLoadedEvent()), ), ], - child: const Scaffold( - body: Padding(padding: EdgeInsets.symmetric(horizontal: 24, vertical: 32), child: BasicConfigurationTab()), + child: Scaffold( + body: BlocConsumer( + listener: (_, __) {}, + builder: (_, state) { + if (state.isLocked) { + return MouseRegion(cursor: SystemMouseCursors.forbidden, child: AbsorbPointer(child: _body)); + } + + return _body; + }, + ), ), ), ); diff --git a/lib/vanila/adapter/gateway/vanila_api_service.dart b/lib/vanila/adapter/gateway/vanila_api_service.dart index a3fa6fb..0e7d0a4 100644 --- a/lib/vanila/adapter/gateway/vanila_api_service.dart +++ b/lib/vanila/adapter/gateway/vanila_api_service.dart @@ -1,9 +1,10 @@ import 'dart:typed_data'; +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; abstract interface class VanilaApiService { Future> fetchGameVersionList(); - Future fetchServerFile(Uri url); + Future fetchServerFile(Uri url, {DownloadProgressCallback? onProgressChanged}); } diff --git a/lib/vanila/adapter/gateway/vanila_repository_impl.dart b/lib/vanila/adapter/gateway/vanila_repository_impl.dart index f60e44e..4edcdd0 100644 --- a/lib/vanila/adapter/gateway/vanila_repository_impl.dart +++ b/lib/vanila/adapter/gateway/vanila_repository_impl.dart @@ -1,6 +1,7 @@ import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_file_storage.dart'; import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; class VanilaRepositoryImpl implements VanilaRepository { @@ -13,8 +14,12 @@ class VanilaRepositoryImpl implements VanilaRepository { Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); @override - Future downloadServerFile(GameVersion version, String savePath) async { - final fileBytes = await _gameVersionApiService.fetchServerFile(version.url); + Future downloadServerFile( + GameVersion version, + String savePath, { + DownloadProgressCallback? onProgressChanged, + }) async { + final fileBytes = await _gameVersionApiService.fetchServerFile(version.url, onProgressChanged: onProgressChanged); await _gameVersionFileStorage.saveFile(fileBytes, savePath); } } diff --git a/lib/vanila/adapter/presentation/vanila_bloc.dart b/lib/vanila/adapter/presentation/vanila_bloc.dart index 65be91d..f436bda 100644 --- a/lib/vanila/adapter/presentation/vanila_bloc.dart +++ b/lib/vanila/adapter/presentation/vanila_bloc.dart @@ -15,9 +15,8 @@ class VanilaBloc extends Bloc { try { final gameVersions = await _getGameVersionListUseCase(); emit( - VanilaState( + const VanilaState.empty().copyWith( gameVersions: gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList(), - selectedGameVersion: null, ), ); } on Exception { @@ -35,7 +34,23 @@ class VanilaBloc extends Bloc { return; } - await _downloadServerFileUseCase(gameVersion.toEntity(), path.join('.', Constants.serverFileName)); + emit(state.copyWith(isLocked: true)); + await _downloadServerFileUseCase( + gameVersion.toEntity(), + path.join('.', Constants.serverFileName), + onProgressChanged: (progress) => add(_VanilaDownloadProgressChangedEvent(progress)), + ); + emit(state.copyWith(isLocked: false)); + }); + + on<_VanilaDownloadProgressChangedEvent>((event, emit) { + if (event.progress < 0) { + emit(state.copyWith(downloadProgress: 0)); + } else if (event.progress > 1) { + emit(state.copyWith(downloadProgress: 1)); + } else { + emit(state.copyWith(downloadProgress: event.progress)); + } }); } } @@ -51,3 +66,9 @@ class VanilaGameVersionSelectedEvent extends VanilaEvent { } class VanilaServerFileDownloadedEvent extends VanilaEvent {} + +class _VanilaDownloadProgressChangedEvent extends VanilaEvent { + final double progress; + + _VanilaDownloadProgressChangedEvent(this.progress); +} diff --git a/lib/vanila/adapter/presentation/vanila_state.dart b/lib/vanila/adapter/presentation/vanila_state.dart index ee1ca90..0c46e45 100644 --- a/lib/vanila/adapter/presentation/vanila_state.dart +++ b/lib/vanila/adapter/presentation/vanila_state.dart @@ -1,23 +1,38 @@ - import 'package:equatable/equatable.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; class VanilaState with EquatableMixin { + final bool isLocked; + final double downloadProgress; final List gameVersions; final GameVersionViewModel? selectedGameVersion; - const VanilaState({required this.gameVersions, required this.selectedGameVersion}); + const VanilaState({ + required this.isLocked, + required this.downloadProgress, + required this.gameVersions, + required this.selectedGameVersion, + }); - const VanilaState.empty() : this(gameVersions: const [], selectedGameVersion: null); + const VanilaState.empty() + : this(isLocked: false, downloadProgress: 0, gameVersions: const [], selectedGameVersion: null); @override - List get props => [gameVersions, selectedGameVersion]; + List get props => [isLocked, downloadProgress, gameVersions, selectedGameVersion]; bool get isGameVersionSelected => selectedGameVersion != null; - VanilaState copyWith({List? gameVersions, GameVersionViewModel? selectedGameVersion}) => - VanilaState( - gameVersions: gameVersions ?? this.gameVersions, - selectedGameVersion: selectedGameVersion ?? this.selectedGameVersion, - ); -} \ No newline at end of file + bool get isDownloading => downloadProgress > 0 && downloadProgress < 1; + + VanilaState copyWith({ + bool? isLocked, + double? downloadProgress, + List? gameVersions, + GameVersionViewModel? selectedGameVersion, + }) => VanilaState( + isLocked: isLocked ?? this.isLocked, + downloadProgress: downloadProgress ?? this.downloadProgress, + gameVersions: gameVersions ?? this.gameVersions, + selectedGameVersion: selectedGameVersion ?? this.selectedGameVersion, + ); +} diff --git a/lib/vanila/application/repository/vanila_repository.dart b/lib/vanila/application/repository/vanila_repository.dart index faa8b62..436f145 100644 --- a/lib/vanila/application/repository/vanila_repository.dart +++ b/lib/vanila/application/repository/vanila_repository.dart @@ -1,7 +1,8 @@ +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; abstract interface class VanilaRepository { Future> getGameVersionList(); - Future downloadServerFile(GameVersion version, String savePath); + Future downloadServerFile(GameVersion version, String savePath, {DownloadProgressCallback? onProgressChanged}); } diff --git a/lib/vanila/application/use_case/download_server_file_use_case.dart b/lib/vanila/application/use_case/download_server_file_use_case.dart index e01d2a8..89ebf4a 100644 --- a/lib/vanila/application/use_case/download_server_file_use_case.dart +++ b/lib/vanila/application/use_case/download_server_file_use_case.dart @@ -1,10 +1,13 @@ import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; +typedef DownloadProgressCallback = void Function(double progress); + class DownloadServerFileUseCase { final VanilaRepository _gameVersionRepository; DownloadServerFileUseCase(this._gameVersionRepository); - Future call(GameVersion version, String savePath) => _gameVersionRepository.downloadServerFile(version, savePath); + Future call(GameVersion version, String savePath, {DownloadProgressCallback? onProgressChanged}) => + _gameVersionRepository.downloadServerFile(version, savePath, onProgressChanged: onProgressChanged); } diff --git a/lib/vanila/framework/api/vanila_api_service_impl.dart b/lib/vanila/framework/api/vanila_api_service_impl.dart index 19fd8c1..d288ab0 100644 --- a/lib/vanila/framework/api/vanila_api_service_impl.dart +++ b/lib/vanila/framework/api/vanila_api_service_impl.dart @@ -1,13 +1,16 @@ +import 'dart:async'; import 'dart:typed_data'; import 'package:http/http.dart' as http; +import 'package:minecraft_server_installer/main/constants.dart'; import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; +import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; class VanilaApiServiceImpl implements VanilaApiService { @override Future> fetchGameVersionList() async { - final sourceUrl = Uri.parse('https://www.dropbox.com/s/mtz3moc9dpjtz7s/GameVersions.txt?dl=1'); + final sourceUrl = Uri.parse(Constants.gameVersionListUrl); final response = await http.get(sourceUrl); final rawGameVersionList = response.body.split('\n'); @@ -21,8 +24,34 @@ class VanilaApiServiceImpl implements VanilaApiService { } @override - Future fetchServerFile(Uri url) async { - final response = await http.get(url); - return response.bodyBytes; + Future fetchServerFile(Uri url, {DownloadProgressCallback? onProgressChanged}) async { + final client = http.Client(); + final request = http.Request('GET', url); + final response = await client.send(request); + + final contentLength = response.contentLength; + final completer = Completer(); + final bytes = []; + var receivedBytes = 0; + + response.stream.listen( + (chunk) { + bytes.addAll(chunk); + receivedBytes += chunk.length; + if (onProgressChanged != null && contentLength != null) { + onProgressChanged(receivedBytes / contentLength); + } + }, + onDone: () { + if (onProgressChanged != null) { + onProgressChanged(1); + } + completer.complete(Uint8List.fromList(bytes)); + }, + onError: completer.completeError, + cancelOnError: true, + ); + + return completer.future; } } diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart index 7f5b8af..500800b 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -5,20 +5,9 @@ import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bl import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; -class GameVersionDropdown extends StatefulWidget { +class GameVersionDropdown extends StatelessWidget { const GameVersionDropdown({super.key}); - @override - State createState() => _GameVersionDropdownState(); -} - -class _GameVersionDropdownState extends State { - @override - void initState() { - super.initState(); - context.read().add(VanilaGameVersionListLoadedEvent()); - } - @override Widget build(BuildContext context) => BlocConsumer( listener: (_, __) {}, diff --git a/pubspec.lock b/pubspec.lock index fe23d2a..f8cf391 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -99,6 +99,14 @@ packages: description: flutter source: sdk version: "0.0.0" + gap: + dependency: "direct main" + description: + name: gap + sha256: f19387d4e32f849394758b91377f9153a1b41d79513ef7668c088c77dbc6955d + url: "https://pub.dev" + source: hosted + version: "3.0.1" http: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 7c90d05..8c714c9 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: minecraft_server_installer description: "A tool that makes installing a Minecraft Server easier." # The following line prevents the package from being accidentally published to # pub.dev using `flutter pub publish`. This is preferred for private packages. -publish_to: 'none' # Remove this line if you wish to publish to pub.dev +publish_to: "none" # Remove this line if you wish to publish to pub.dev # The following defines the version and build number for your application. # A version number is three numbers separated by dots, like 1.2.43 @@ -36,6 +36,7 @@ dependencies: cupertino_icons: ^1.0.8 equatable: ^2.0.7 flutter_bloc: ^9.1.1 + gap: ^3.0.1 http: ^1.4.0 path: ^1.9.1 @@ -55,7 +56,6 @@ dev_dependencies: # The following section is specific to Flutter packages. flutter: - # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in # the material Icons class. From c03af1c33cd6fbf8f6086c727d0368f69a2aea3e Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 18:07:54 +0800 Subject: [PATCH 10/12] MCSI-1 fix: dropdown menu is cleared --- lib/vanila/framework/ui/game_version_dropdown.dart | 3 ++- test/widget_test.dart | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 test/widget_test.dart diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanila/framework/ui/game_version_dropdown.dart index 500800b..8738939 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanila/framework/ui/game_version_dropdown.dart @@ -13,10 +13,11 @@ class GameVersionDropdown extends StatelessWidget { listener: (_, __) {}, builder: (_, state) => DropdownMenu( + initialSelection: state.selectedGameVersion, enabled: state.gameVersions.isNotEmpty, requestFocusOnTap: false, expandedInsets: EdgeInsets.zero, - label: const Text(Strings.fieldGameVersion), + label: const Text('${Strings.fieldGameVersion} *'), onSelected: (value) { if (value != null) { context.read().add(VanilaGameVersionSelectedEvent(value)); diff --git a/test/widget_test.dart b/test/widget_test.dart deleted file mode 100644 index 2a2b819..0000000 --- a/test/widget_test.dart +++ /dev/null @@ -1,8 +0,0 @@ -// This is a basic Flutter widget test. -// -// To perform an interaction with a widget in your test, use the WidgetTester -// utility in the flutter_test package. For example, you can send tap and scroll -// gestures. You can also use WidgetTester to find child widgets in the widget -// tree, read text, and verify that the values of widget properties are correct. - -void main() {} From b1aa09bf5791c442dbee453298ce4889d0030dc1 Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 18:15:55 +0800 Subject: [PATCH 11/12] MCSI-1 feat: adjust window size --- lib/main/main.dart | 19 +++++++++- linux/runner/my_application.cc | 29 ++------------- pubspec.lock | 64 ++++++++++++++++++++++++++++++++++ pubspec.yaml | 1 + 4 files changed, 85 insertions(+), 28 deletions(-) diff --git a/lib/main/main.dart b/lib/main/main.dart index 96db365..b3aa558 100644 --- a/lib/main/main.dart +++ b/lib/main/main.dart @@ -1,6 +1,23 @@ import 'package:flutter/material.dart'; import 'package:minecraft_server_installer/main/framework/ui/minecraft_server_installer.dart'; +import 'package:window_manager/window_manager.dart'; + +Future main() async { + WidgetsFlutterBinding.ensureInitialized(); + await windowManager.ensureInitialized(); + + final windowOptions = const WindowOptions( + size: Size(400, 600), + minimumSize: Size(400, 600), + center: true, + backgroundColor: Colors.transparent, + skipTaskbar: false, + titleBarStyle: TitleBarStyle.hidden, + ); + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); -void main() { runApp(const MinecraftServerInstaller()); } diff --git a/linux/runner/my_application.cc b/linux/runner/my_application.cc index ac27961..b1cd8f2 100644 --- a/linux/runner/my_application.cc +++ b/linux/runner/my_application.cc @@ -20,34 +20,9 @@ static void my_application_activate(GApplication* application) { GtkWindow* window = GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); - // Use a header bar when running in GNOME as this is the common style used - // by applications and is the setup most users will be using (e.g. Ubuntu - // desktop). - // If running on X and not using GNOME then just use a traditional title bar - // in case the window manager does more exotic layout, e.g. tiling. - // If running on Wayland assume the header bar will work (may need changing - // if future cases occur). - gboolean use_header_bar = TRUE; -#ifdef GDK_WINDOWING_X11 - GdkScreen* screen = gtk_window_get_screen(window); - if (GDK_IS_X11_SCREEN(screen)) { - const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); - if (g_strcmp0(wm_name, "GNOME Shell") != 0) { - use_header_bar = FALSE; - } - } -#endif - if (use_header_bar) { - GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); - gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "minecraft_server_installer"); - gtk_header_bar_set_show_close_button(header_bar, TRUE); - gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); - } else { - gtk_window_set_title(window, "minecraft_server_installer"); - } + gtk_window_set_decorated(window, FALSE); - gtk_window_set_default_size(window, 1280, 720); + gtk_window_set_default_size(window, 400, 600); gtk_widget_show(GTK_WIDGET(window)); g_autoptr(FlDartProject) project = fl_dart_project_new(); diff --git a/pubspec.lock b/pubspec.lock index f8cf391..d89613a 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -123,6 +123,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" + url: "https://pub.dev" + source: hosted + version: "4.9.0" leak_tracker: dependency: transitive description: @@ -195,6 +203,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + plugin_platform_interface: + dependency: transitive + description: + name: plugin_platform_interface + sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + url: "https://pub.dev" + source: hosted + version: "2.1.8" provider: dependency: transitive description: @@ -203,6 +219,46 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.5" + screen_retriever: + dependency: transitive + description: + name: screen_retriever + sha256: "570dbc8e4f70bac451e0efc9c9bb19fa2d6799a11e6ef04f946d7886d2e23d0c" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_linux: + dependency: transitive + description: + name: screen_retriever_linux + sha256: f7f8120c92ef0784e58491ab664d01efda79a922b025ff286e29aa123ea3dd18 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_macos: + dependency: transitive + description: + name: screen_retriever_macos + sha256: "71f956e65c97315dd661d71f828708bd97b6d358e776f1a30d5aa7d22d78a149" + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_platform_interface: + dependency: transitive + description: + name: screen_retriever_platform_interface + sha256: ee197f4581ff0d5608587819af40490748e1e39e648d7680ecf95c05197240c0 + url: "https://pub.dev" + source: hosted + version: "0.2.0" + screen_retriever_windows: + dependency: transitive + description: + name: screen_retriever_windows + sha256: "449ee257f03ca98a57288ee526a301a430a344a161f9202b4fcc38576716fe13" + url: "https://pub.dev" + source: hosted + version: "0.2.0" sky_engine: dependency: transitive description: flutter @@ -288,6 +344,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" + window_manager: + dependency: "direct main" + description: + name: window_manager + sha256: "51d50168ab267d344b975b15390426b1243600d436770d3f13de67e55b05ec16" + url: "https://pub.dev" + source: hosted + version: "0.5.0" sdks: dart: ">=3.7.2 <4.0.0" flutter: ">=3.18.0-18.0.pre.54" diff --git a/pubspec.yaml b/pubspec.yaml index 8c714c9..a19fc57 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -39,6 +39,7 @@ dependencies: gap: ^3.0.1 http: ^1.4.0 path: ^1.9.1 + window_manager: ^0.5.0 dev_dependencies: flutter_test: From 6e85c7ff0669f8d8c62e80fb5e6771b8c5a401da Mon Sep 17 00:00:00 2001 From: SquidSpirit Date: Sun, 29 Jun 2025 18:31:41 +0800 Subject: [PATCH 12/12] MCSI-1 refactor: rename vanila -> vanilla --- .../framework/ui/basic_configuration_tab.dart | 12 +-- .../ui/minecraft_server_installer.dart | 28 +++---- .../adapter/gateway/vanila_api_service.dart | 10 --- .../gateway/vanila_repository_impl.dart | 25 ------- .../adapter/presentation/vanila_bloc.dart | 74 ------------------- .../repository/vanila_repository.dart | 8 -- .../get_game_version_list_use_case.dart | 10 --- .../adapter/gateway/vanilla_api_service.dart | 10 +++ .../gateway/vanilla_file_storage.dart} | 2 +- .../gateway/vanilla_repository_impl.dart | 25 +++++++ .../presentation/game_version_view_model.dart | 2 +- .../adapter/presentation/vanilla_bloc.dart | 74 +++++++++++++++++++ .../adapter/presentation/vanilla_state.dart} | 12 +-- .../repository/vanilla_repository.dart | 8 ++ .../download_server_file_use_case.dart | 6 +- .../get_game_version_list_use_case.dart | 10 +++ .../domain/entity/game_version.dart | 0 .../api/vanilla_api_service_impl.dart} | 8 +- .../storage/vanilla_file_storage_impl.dart} | 4 +- .../framework/ui/game_version_dropdown.dart | 10 +-- 20 files changed, 169 insertions(+), 169 deletions(-) delete mode 100644 lib/vanila/adapter/gateway/vanila_api_service.dart delete mode 100644 lib/vanila/adapter/gateway/vanila_repository_impl.dart delete mode 100644 lib/vanila/adapter/presentation/vanila_bloc.dart delete mode 100644 lib/vanila/application/repository/vanila_repository.dart delete mode 100644 lib/vanila/application/use_case/get_game_version_list_use_case.dart create mode 100644 lib/vanilla/adapter/gateway/vanilla_api_service.dart rename lib/{vanila/adapter/gateway/vanila_file_storage.dart => vanilla/adapter/gateway/vanilla_file_storage.dart} (66%) create mode 100644 lib/vanilla/adapter/gateway/vanilla_repository_impl.dart rename lib/{vanila => vanilla}/adapter/presentation/game_version_view_model.dart (83%) create mode 100644 lib/vanilla/adapter/presentation/vanilla_bloc.dart rename lib/{vanila/adapter/presentation/vanila_state.dart => vanilla/adapter/presentation/vanilla_state.dart} (81%) create mode 100644 lib/vanilla/application/repository/vanilla_repository.dart rename lib/{vanila => vanilla}/application/use_case/download_server_file_use_case.dart (61%) create mode 100644 lib/vanilla/application/use_case/get_game_version_list_use_case.dart rename lib/{vanila => vanilla}/domain/entity/game_version.dart (100%) rename lib/{vanila/framework/api/vanila_api_service_impl.dart => vanilla/framework/api/vanilla_api_service_impl.dart} (82%) rename lib/{vanila/framework/storage/vanila_file_storage_impl.dart => vanilla/framework/storage/vanilla_file_storage_impl.dart} (68%) rename lib/{vanila => vanilla}/framework/ui/game_version_dropdown.dart (68%) diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index c29173b..8ce10fd 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -2,10 +2,10 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:gap/gap.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; -import 'package:minecraft_server_installer/vanila/framework/ui/game_version_dropdown.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_bloc.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_state.dart'; +import 'package:minecraft_server_installer/vanilla/framework/ui/game_version_dropdown.dart'; class BasicConfigurationTab extends StatefulWidget { const BasicConfigurationTab({super.key}); @@ -23,7 +23,7 @@ class _BasicConfigurationTabState extends State { children: [ const GameVersionDropdown(), const Spacer(), - BlocConsumer( + BlocConsumer( listener: (_, __) {}, builder: (context, state) => Row( @@ -50,6 +50,6 @@ class _BasicConfigurationTabState extends State { } void _downloadServerFile() { - context.read().add(VanilaServerFileDownloadedEvent()); + context.read().add(VanillaServerFileDownloadedEvent()); } } diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index 3151b1b..190ba96 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -1,13 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_repository_impl.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -import 'package:minecraft_server_installer/vanila/framework/api/vanila_api_service_impl.dart'; -import 'package:minecraft_server_installer/vanila/framework/storage/vanila_file_storage_impl.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_repository_impl.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_bloc.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_state.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/get_game_version_list_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/framework/api/vanilla_api_service_impl.dart'; +import 'package:minecraft_server_installer/vanilla/framework/storage/vanilla_file_storage_impl.dart'; class MinecraftServerInstaller extends StatelessWidget { const MinecraftServerInstaller({super.key}); @@ -17,9 +17,9 @@ class MinecraftServerInstaller extends StatelessWidget { @override Widget build(BuildContext context) { - final gameVersionApiService = VanilaApiServiceImpl(); - final gameVersionFileStorage = VanilaFileStorageImpl(); - final gameVersionRepository = VanilaRepositoryImpl(gameVersionApiService, gameVersionFileStorage); + final gameVersionApiService = VanillaApiServiceImpl(); + final gameVersionFileStorage = VanillaFileStorageImpl(); + final gameVersionRepository = VanillaRepositoryImpl(gameVersionApiService, gameVersionFileStorage); final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); final downloadServerFileUseCase = DownloadServerFileUseCase(gameVersionRepository); @@ -28,15 +28,15 @@ class MinecraftServerInstaller extends StatelessWidget { theme: ThemeData.light().copyWith(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade900)), home: MultiBlocProvider( providers: [ - BlocProvider( + BlocProvider( create: (context) => - VanilaBloc(getGameVersionListUseCase, downloadServerFileUseCase) - ..add(VanilaGameVersionListLoadedEvent()), + VanillaBloc(getGameVersionListUseCase, downloadServerFileUseCase) + ..add(VanillaGameVersionListLoadedEvent()), ), ], child: Scaffold( - body: BlocConsumer( + body: BlocConsumer( listener: (_, __) {}, builder: (_, state) { if (state.isLocked) { diff --git a/lib/vanila/adapter/gateway/vanila_api_service.dart b/lib/vanila/adapter/gateway/vanila_api_service.dart deleted file mode 100644 index 0e7d0a4..0000000 --- a/lib/vanila/adapter/gateway/vanila_api_service.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'dart:typed_data'; - -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; - -abstract interface class VanilaApiService { - Future> fetchGameVersionList(); - - Future fetchServerFile(Uri url, {DownloadProgressCallback? onProgressChanged}); -} diff --git a/lib/vanila/adapter/gateway/vanila_repository_impl.dart b/lib/vanila/adapter/gateway/vanila_repository_impl.dart deleted file mode 100644 index 4edcdd0..0000000 --- a/lib/vanila/adapter/gateway/vanila_repository_impl.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_file_storage.dart'; -import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; - -class VanilaRepositoryImpl implements VanilaRepository { - final VanilaApiService _gameVersionApiService; - final VanilaFileStorage _gameVersionFileStorage; - - VanilaRepositoryImpl(this._gameVersionApiService, this._gameVersionFileStorage); - - @override - Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); - - @override - Future downloadServerFile( - GameVersion version, - String savePath, { - DownloadProgressCallback? onProgressChanged, - }) async { - final fileBytes = await _gameVersionApiService.fetchServerFile(version.url, onProgressChanged: onProgressChanged); - await _gameVersionFileStorage.saveFile(fileBytes, savePath); - } -} diff --git a/lib/vanila/adapter/presentation/vanila_bloc.dart b/lib/vanila/adapter/presentation/vanila_bloc.dart deleted file mode 100644 index f436bda..0000000 --- a/lib/vanila/adapter/presentation/vanila_bloc.dart +++ /dev/null @@ -1,74 +0,0 @@ -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:minecraft_server_installer/main/constants.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/get_game_version_list_use_case.dart'; -import 'package:path/path.dart' as path; - -class VanilaBloc extends Bloc { - final GetGameVersionListUseCase _getGameVersionListUseCase; - final DownloadServerFileUseCase _downloadServerFileUseCase; - - VanilaBloc(this._getGameVersionListUseCase, this._downloadServerFileUseCase) : super(const VanilaState.empty()) { - on((_, emit) async { - try { - final gameVersions = await _getGameVersionListUseCase(); - emit( - const VanilaState.empty().copyWith( - gameVersions: gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList(), - ), - ); - } on Exception { - emit(const VanilaState.empty()); - } - }); - - on((event, emit) { - emit(state.copyWith(selectedGameVersion: event.gameVersion)); - }); - - on((_, emit) async { - final gameVersion = state.selectedGameVersion; - if (gameVersion == null) { - return; - } - - emit(state.copyWith(isLocked: true)); - await _downloadServerFileUseCase( - gameVersion.toEntity(), - path.join('.', Constants.serverFileName), - onProgressChanged: (progress) => add(_VanilaDownloadProgressChangedEvent(progress)), - ); - emit(state.copyWith(isLocked: false)); - }); - - on<_VanilaDownloadProgressChangedEvent>((event, emit) { - if (event.progress < 0) { - emit(state.copyWith(downloadProgress: 0)); - } else if (event.progress > 1) { - emit(state.copyWith(downloadProgress: 1)); - } else { - emit(state.copyWith(downloadProgress: event.progress)); - } - }); - } -} - -sealed class VanilaEvent {} - -class VanilaGameVersionListLoadedEvent extends VanilaEvent {} - -class VanilaGameVersionSelectedEvent extends VanilaEvent { - final GameVersionViewModel gameVersion; - - VanilaGameVersionSelectedEvent(this.gameVersion); -} - -class VanilaServerFileDownloadedEvent extends VanilaEvent {} - -class _VanilaDownloadProgressChangedEvent extends VanilaEvent { - final double progress; - - _VanilaDownloadProgressChangedEvent(this.progress); -} diff --git a/lib/vanila/application/repository/vanila_repository.dart b/lib/vanila/application/repository/vanila_repository.dart deleted file mode 100644 index 436f145..0000000 --- a/lib/vanila/application/repository/vanila_repository.dart +++ /dev/null @@ -1,8 +0,0 @@ -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; - -abstract interface class VanilaRepository { - Future> getGameVersionList(); - - Future downloadServerFile(GameVersion version, String savePath, {DownloadProgressCallback? onProgressChanged}); -} diff --git a/lib/vanila/application/use_case/get_game_version_list_use_case.dart b/lib/vanila/application/use_case/get_game_version_list_use_case.dart deleted file mode 100644 index b99349f..0000000 --- a/lib/vanila/application/use_case/get_game_version_list_use_case.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; - -class GetGameVersionListUseCase { - final VanilaRepository _gameVersionRepository; - - GetGameVersionListUseCase(this._gameVersionRepository); - - Future> call() => _gameVersionRepository.getGameVersionList(); -} diff --git a/lib/vanilla/adapter/gateway/vanilla_api_service.dart b/lib/vanilla/adapter/gateway/vanilla_api_service.dart new file mode 100644 index 0000000..2c50673 --- /dev/null +++ b/lib/vanilla/adapter/gateway/vanilla_api_service.dart @@ -0,0 +1,10 @@ +import 'dart:typed_data'; + +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; + +abstract interface class VanillaApiService { + Future> fetchGameVersionList(); + + Future fetchServerFile(Uri url, {DownloadProgressCallback? onProgressChanged}); +} diff --git a/lib/vanila/adapter/gateway/vanila_file_storage.dart b/lib/vanilla/adapter/gateway/vanilla_file_storage.dart similarity index 66% rename from lib/vanila/adapter/gateway/vanila_file_storage.dart rename to lib/vanilla/adapter/gateway/vanilla_file_storage.dart index 7ba203d..3217984 100644 --- a/lib/vanila/adapter/gateway/vanila_file_storage.dart +++ b/lib/vanilla/adapter/gateway/vanilla_file_storage.dart @@ -1,5 +1,5 @@ import 'dart:typed_data'; -abstract interface class VanilaFileStorage { +abstract interface class VanillaFileStorage { Future saveFile(Uint8List fileBytes, String savePath); } diff --git a/lib/vanilla/adapter/gateway/vanilla_repository_impl.dart b/lib/vanilla/adapter/gateway/vanilla_repository_impl.dart new file mode 100644 index 0000000..3bd2dca --- /dev/null +++ b/lib/vanilla/adapter/gateway/vanilla_repository_impl.dart @@ -0,0 +1,25 @@ +import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_api_service.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_file_storage.dart'; +import 'package:minecraft_server_installer/vanilla/application/repository/vanilla_repository.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; + +class VanillaRepositoryImpl implements VanillaRepository { + final VanillaApiService _gameVersionApiService; + final VanillaFileStorage _gameVersionFileStorage; + + VanillaRepositoryImpl(this._gameVersionApiService, this._gameVersionFileStorage); + + @override + Future> getGameVersionList() => _gameVersionApiService.fetchGameVersionList(); + + @override + Future downloadServerFile( + GameVersion version, + String savePath, { + DownloadProgressCallback? onProgressChanged, + }) async { + final fileBytes = await _gameVersionApiService.fetchServerFile(version.url, onProgressChanged: onProgressChanged); + await _gameVersionFileStorage.saveFile(fileBytes, savePath); + } +} diff --git a/lib/vanila/adapter/presentation/game_version_view_model.dart b/lib/vanilla/adapter/presentation/game_version_view_model.dart similarity index 83% rename from lib/vanila/adapter/presentation/game_version_view_model.dart rename to lib/vanilla/adapter/presentation/game_version_view_model.dart index de8a870..cb50d77 100644 --- a/lib/vanila/adapter/presentation/game_version_view_model.dart +++ b/lib/vanilla/adapter/presentation/game_version_view_model.dart @@ -1,5 +1,5 @@ import 'package:equatable/equatable.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; class GameVersionViewModel with EquatableMixin { final String name; diff --git a/lib/vanilla/adapter/presentation/vanilla_bloc.dart b/lib/vanilla/adapter/presentation/vanilla_bloc.dart new file mode 100644 index 0000000..23cb010 --- /dev/null +++ b/lib/vanilla/adapter/presentation/vanilla_bloc.dart @@ -0,0 +1,74 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/main/constants.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_state.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/get_game_version_list_use_case.dart'; +import 'package:path/path.dart' as path; + +class VanillaBloc extends Bloc { + final GetGameVersionListUseCase _getGameVersionListUseCase; + final DownloadServerFileUseCase _downloadServerFileUseCase; + + VanillaBloc(this._getGameVersionListUseCase, this._downloadServerFileUseCase) : super(const VanillaState.empty()) { + on((_, emit) async { + try { + final gameVersions = await _getGameVersionListUseCase(); + emit( + const VanillaState.empty().copyWith( + gameVersions: gameVersions.map((entity) => GameVersionViewModel.from(entity)).toList(), + ), + ); + } on Exception { + emit(const VanillaState.empty()); + } + }); + + on((event, emit) { + emit(state.copyWith(selectedGameVersion: event.gameVersion)); + }); + + on((_, emit) async { + final gameVersion = state.selectedGameVersion; + if (gameVersion == null) { + return; + } + + emit(state.copyWith(isLocked: true)); + await _downloadServerFileUseCase( + gameVersion.toEntity(), + path.join('.', Constants.serverFileName), + onProgressChanged: (progress) => add(_VanillaDownloadProgressChangedEvent(progress)), + ); + emit(state.copyWith(isLocked: false)); + }); + + on<_VanillaDownloadProgressChangedEvent>((event, emit) { + if (event.progress < 0) { + emit(state.copyWith(downloadProgress: 0)); + } else if (event.progress > 1) { + emit(state.copyWith(downloadProgress: 1)); + } else { + emit(state.copyWith(downloadProgress: event.progress)); + } + }); + } +} + +sealed class VanillaEvent {} + +class VanillaGameVersionListLoadedEvent extends VanillaEvent {} + +class VanillaGameVersionSelectedEvent extends VanillaEvent { + final GameVersionViewModel gameVersion; + + VanillaGameVersionSelectedEvent(this.gameVersion); +} + +class VanillaServerFileDownloadedEvent extends VanillaEvent {} + +class _VanillaDownloadProgressChangedEvent extends VanillaEvent { + final double progress; + + _VanillaDownloadProgressChangedEvent(this.progress); +} diff --git a/lib/vanila/adapter/presentation/vanila_state.dart b/lib/vanilla/adapter/presentation/vanilla_state.dart similarity index 81% rename from lib/vanila/adapter/presentation/vanila_state.dart rename to lib/vanilla/adapter/presentation/vanilla_state.dart index 0c46e45..6ae3e2d 100644 --- a/lib/vanila/adapter/presentation/vanila_state.dart +++ b/lib/vanilla/adapter/presentation/vanilla_state.dart @@ -1,20 +1,20 @@ import 'package:equatable/equatable.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; -class VanilaState with EquatableMixin { +class VanillaState with EquatableMixin { final bool isLocked; final double downloadProgress; final List gameVersions; final GameVersionViewModel? selectedGameVersion; - const VanilaState({ + const VanillaState({ required this.isLocked, required this.downloadProgress, required this.gameVersions, required this.selectedGameVersion, }); - const VanilaState.empty() + const VanillaState.empty() : this(isLocked: false, downloadProgress: 0, gameVersions: const [], selectedGameVersion: null); @override @@ -24,12 +24,12 @@ class VanilaState with EquatableMixin { bool get isDownloading => downloadProgress > 0 && downloadProgress < 1; - VanilaState copyWith({ + VanillaState copyWith({ bool? isLocked, double? downloadProgress, List? gameVersions, GameVersionViewModel? selectedGameVersion, - }) => VanilaState( + }) => VanillaState( isLocked: isLocked ?? this.isLocked, downloadProgress: downloadProgress ?? this.downloadProgress, gameVersions: gameVersions ?? this.gameVersions, diff --git a/lib/vanilla/application/repository/vanilla_repository.dart b/lib/vanilla/application/repository/vanilla_repository.dart new file mode 100644 index 0000000..f9c0bcb --- /dev/null +++ b/lib/vanilla/application/repository/vanilla_repository.dart @@ -0,0 +1,8 @@ +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; + +abstract interface class VanillaRepository { + Future> getGameVersionList(); + + Future downloadServerFile(GameVersion version, String savePath, {DownloadProgressCallback? onProgressChanged}); +} diff --git a/lib/vanila/application/use_case/download_server_file_use_case.dart b/lib/vanilla/application/use_case/download_server_file_use_case.dart similarity index 61% rename from lib/vanila/application/use_case/download_server_file_use_case.dart rename to lib/vanilla/application/use_case/download_server_file_use_case.dart index 89ebf4a..431ef72 100644 --- a/lib/vanila/application/use_case/download_server_file_use_case.dart +++ b/lib/vanilla/application/use_case/download_server_file_use_case.dart @@ -1,10 +1,10 @@ -import 'package:minecraft_server_installer/vanila/application/repository/vanila_repository.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; +import 'package:minecraft_server_installer/vanilla/application/repository/vanilla_repository.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; typedef DownloadProgressCallback = void Function(double progress); class DownloadServerFileUseCase { - final VanilaRepository _gameVersionRepository; + final VanillaRepository _gameVersionRepository; DownloadServerFileUseCase(this._gameVersionRepository); diff --git a/lib/vanilla/application/use_case/get_game_version_list_use_case.dart b/lib/vanilla/application/use_case/get_game_version_list_use_case.dart new file mode 100644 index 0000000..3f4fcb5 --- /dev/null +++ b/lib/vanilla/application/use_case/get_game_version_list_use_case.dart @@ -0,0 +1,10 @@ +import 'package:minecraft_server_installer/vanilla/application/repository/vanilla_repository.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; + +class GetGameVersionListUseCase { + final VanillaRepository _gameVersionRepository; + + GetGameVersionListUseCase(this._gameVersionRepository); + + Future> call() => _gameVersionRepository.getGameVersionList(); +} diff --git a/lib/vanila/domain/entity/game_version.dart b/lib/vanilla/domain/entity/game_version.dart similarity index 100% rename from lib/vanila/domain/entity/game_version.dart rename to lib/vanilla/domain/entity/game_version.dart diff --git a/lib/vanila/framework/api/vanila_api_service_impl.dart b/lib/vanilla/framework/api/vanilla_api_service_impl.dart similarity index 82% rename from lib/vanila/framework/api/vanila_api_service_impl.dart rename to lib/vanilla/framework/api/vanilla_api_service_impl.dart index d288ab0..d633312 100644 --- a/lib/vanila/framework/api/vanila_api_service_impl.dart +++ b/lib/vanilla/framework/api/vanilla_api_service_impl.dart @@ -3,11 +3,11 @@ import 'dart:typed_data'; import 'package:http/http.dart' as http; import 'package:minecraft_server_installer/main/constants.dart'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_api_service.dart'; -import 'package:minecraft_server_installer/vanila/application/use_case/download_server_file_use_case.dart'; -import 'package:minecraft_server_installer/vanila/domain/entity/game_version.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_api_service.dart'; +import 'package:minecraft_server_installer/vanilla/application/use_case/download_server_file_use_case.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; -class VanilaApiServiceImpl implements VanilaApiService { +class VanillaApiServiceImpl implements VanillaApiService { @override Future> fetchGameVersionList() async { final sourceUrl = Uri.parse(Constants.gameVersionListUrl); diff --git a/lib/vanila/framework/storage/vanila_file_storage_impl.dart b/lib/vanilla/framework/storage/vanilla_file_storage_impl.dart similarity index 68% rename from lib/vanila/framework/storage/vanila_file_storage_impl.dart rename to lib/vanilla/framework/storage/vanilla_file_storage_impl.dart index 656379e..921aa92 100644 --- a/lib/vanila/framework/storage/vanila_file_storage_impl.dart +++ b/lib/vanilla/framework/storage/vanilla_file_storage_impl.dart @@ -1,9 +1,9 @@ import 'dart:io'; import 'dart:typed_data'; -import 'package:minecraft_server_installer/vanila/adapter/gateway/vanila_file_storage.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_file_storage.dart'; -class VanilaFileStorageImpl implements VanilaFileStorage { +class VanillaFileStorageImpl implements VanillaFileStorage { @override Future saveFile(Uint8List fileBytes, String savePath) async { final file = File(savePath); diff --git a/lib/vanila/framework/ui/game_version_dropdown.dart b/lib/vanilla/framework/ui/game_version_dropdown.dart similarity index 68% rename from lib/vanila/framework/ui/game_version_dropdown.dart rename to lib/vanilla/framework/ui/game_version_dropdown.dart index 8738939..2d9b9b7 100644 --- a/lib/vanila/framework/ui/game_version_dropdown.dart +++ b/lib/vanilla/framework/ui/game_version_dropdown.dart @@ -1,15 +1,15 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_bloc.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/game_version_view_model.dart'; -import 'package:minecraft_server_installer/vanila/adapter/presentation/vanila_state.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_bloc.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; +import 'package:minecraft_server_installer/vanilla/adapter/presentation/vanilla_state.dart'; class GameVersionDropdown extends StatelessWidget { const GameVersionDropdown({super.key}); @override - Widget build(BuildContext context) => BlocConsumer( + Widget build(BuildContext context) => BlocConsumer( listener: (_, __) {}, builder: (_, state) => DropdownMenu( @@ -20,7 +20,7 @@ class GameVersionDropdown extends StatelessWidget { label: const Text('${Strings.fieldGameVersion} *'), onSelected: (value) { if (value != null) { - context.read().add(VanilaGameVersionSelectedEvent(value)); + context.read().add(VanillaGameVersionSelectedEvent(value)); } }, dropdownMenuEntries: