From 2278546f01729689bdab3472fefdc6fd6b7b59ae Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Mon, 10 Apr 2023 13:28:37 +0200 Subject: [PATCH] Update UI --- lib/src/components/components.dart | 22 ++++ .../components/select_gamemode_component.dart | 70 +++++++++++ lib/src/components/select_map_component.dart | 110 ++++++++++++++++++ lib/src/provider/data.dart | 18 +-- .../event/components/dialogs/add_fight.dart | 35 ++---- .../event/components/dialogs/edit_fight.dart | 33 +++--- .../event/components/loaded_event.dart | 32 ++--- .../generator/generators/group_generator.dart | 24 ++-- 8 files changed, 263 insertions(+), 81 deletions(-) create mode 100644 lib/src/components/select_gamemode_component.dart create mode 100644 lib/src/components/select_map_component.dart diff --git a/lib/src/components/components.dart b/lib/src/components/components.dart index a518073..acc917e 100644 --- a/lib/src/components/components.dart +++ b/lib/src/components/components.dart @@ -17,6 +17,28 @@ * along with this program. If not, see . */ +import 'package:flutter/material.dart'; + export 'date_time_editor.dart'; export 'error.dart'; export 'team_chip.dart'; +export 'select_gamemode_component.dart'; +export 'select_map_component.dart'; + +class Disabled extends StatelessWidget { + final String? initialValue; + const Disabled({Key? key, this.initialValue = ""}) : super(key: key); + + @override + Widget build(BuildContext context) { + return DropdownMenu( + width: 200, + label: Text(initialValue!), + enabled: false, + dropdownMenuEntries: [ + if (initialValue != null) + DropdownMenuEntry(value: initialValue, label: initialValue!), + ], + ); + } +} diff --git a/lib/src/components/select_gamemode_component.dart b/lib/src/components/select_gamemode_component.dart new file mode 100644 index 0000000..8853c36 --- /dev/null +++ b/lib/src/components/select_gamemode_component.dart @@ -0,0 +1,70 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/provider/data.dart'; + +import 'components.dart'; + +class SelectGameModeComponent extends HookConsumerWidget { + final String? initialValue; + final void Function(String?) onChanged; + + const SelectGameModeComponent({ + Key? key, + required this.initialValue, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return ref.watch(fightServersProvider).when( + data: (data) { + if (initialValue != null && !data.contains(initialValue)) { + return Disabled( + initialValue: initialValue, + ); + } + + return DropdownMenu( + width: 200, + menuHeight: 300, + initialSelection: initialValue, + onSelected: onChanged, + enableFilter: true, + enableSearch: true, + label: const Text("Game Mode"), + dropdownMenuEntries: [ + const DropdownMenuEntry(value: null, label: ""), + for (final gameMode in data) + DropdownMenuEntry( + value: gameMode, + label: gameMode, + ), + ], + ); + }, + error: (error, stack) => Disabled( + initialValue: initialValue, + ), + loading: () => const Disabled(initialValue: "Game Mode"), + ); + } +} diff --git a/lib/src/components/select_map_component.dart b/lib/src/components/select_map_component.dart new file mode 100644 index 0000000..1486843 --- /dev/null +++ b/lib/src/components/select_map_component.dart @@ -0,0 +1,110 @@ +/* + * This file is a part of the SteamWar software. + * + * Copyright (C) 2023 SteamWar.de-Serverteam + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/components/components.dart'; +import 'package:steamwar_multitool/src/provider/data.dart'; + +class SelectMapComponent extends HookConsumerWidget { + final String? gameMode; + final String? initialValue; + final void Function(String?) onChanged; + + const SelectMapComponent({ + Key? key, + required this.gameMode, + required this.initialValue, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (gameMode == null) { + return const Disabled(initialValue: "Select Map"); + } + + return ProviderScope( + overrides: [ + mapGameModeProvider.overrideWithValue(gameMode!), + mapsProvider + ], + child: _SelectMapInternal( + initialValue: initialValue, + onChanged: onChanged, + ), + ); + } +} + +class _SelectMapInternal extends HookConsumerWidget { + final String? initialValue; + final void Function(String?) onChanged; + + const _SelectMapInternal({ + Key? key, + required this.initialValue, + required this.onChanged, + }) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final maps = ref.watch(mapsProvider); + return maps.when( + data: (data) { + return DropdownMenu( + width: 200, + menuHeight: 300, + initialSelection: initialValue, + onSelected: (value) { + if (value == "%random%") { + onChanged(data[Random().nextInt(data.length)]); + return; + } + onChanged(value); + }, + enableSearch: true, + enableFilter: true, + label: const Text("Select Map"), + dropdownMenuEntries: [ + const DropdownMenuEntry( + value: null, + label: "", + ), + const DropdownMenuEntry( + value: "%random%", + label: "Random", + ), + for (final map in data) + DropdownMenuEntry( + value: map, + label: map, + ), + ], + ); + }, + error: (error, stack) => Disabled( + initialValue: initialValue, + ), + loading: () => const Disabled(initialValue: "Select Map"), + ); + } +} diff --git a/lib/src/provider/data.dart b/lib/src/provider/data.dart index 9f21a28..789c6b4 100644 --- a/lib/src/provider/data.dart +++ b/lib/src/provider/data.dart @@ -43,15 +43,17 @@ final schematicTypesProvider = FutureProvider((ref) async { }); }); -final mapsProvider = FutureProvider>>((ref) async { +final mapGameModeProvider = Provider((ref) { + throw UnimplementedError("mapGameModeProvider"); +}); + +final mapsProvider = FutureProvider>((ref) async { final client = await ref.watch(httpClient.future); - final servers = await ref.watch(fightServersProvider.future); - final ret = >{}; - for (final server in servers) { - final res = await client.get("/data/gamemodes/$server/maps"); - ret[server] = (res.data as List).map((e) => e.toString()).toList(); - } - return ret; + final gameMode = ref.watch(mapGameModeProvider); + return ((await client.get("/data/gamemodes/$gameMode/maps")).data + as List) + .map((e) => e.toString()) + .toList(); }); final usersProvider = FutureProvider((ref) async { diff --git a/lib/src/screens/event/components/dialogs/add_fight.dart b/lib/src/screens/event/components/dialogs/add_fight.dart index 394c5ba..717f97d 100644 --- a/lib/src/screens/event/components/dialogs/add_fight.dart +++ b/lib/src/screens/event/components/dialogs/add_fight.dart @@ -20,7 +20,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/components/components.dart'; import 'package:steamwar_multitool/src/components/date_time_editor.dart'; +import 'package:steamwar_multitool/src/components/select_map_component.dart'; import 'package:steamwar_multitool/src/provider/provider.dart'; import 'package:steamwar_multitool/src/screens/event/components/team_selector.dart'; import 'package:steamwar_multitool/src/delegates/delegates.dart'; @@ -61,33 +63,18 @@ class AddFightDialog extends HookConsumerWidget { const Divider(), const Text("Gamemode"), const SizedBox(height: 8), - ElevatedButton( - onPressed: () async { - final modes = await ref.read(fightServersProvider.future); - final mode = await showSearch( - context: context, delegate: GamemodeSearchDelegate(modes)); - if (mode != null) { - gamemode.value = mode; - } - }, - child: Text(gamemode.value ?? "Select"), - ), + SelectGameModeComponent( + initialValue: gamemode.value, + onChanged: (p0) => gamemode.value = p0), const SizedBox(height: 8), const Text("Map"), const SizedBox(height: 8), - ElevatedButton( - onPressed: gamemode.value != null - ? () async { - final maps = await ref.read(mapsProvider.future); - final sMap = await showSearch( - context: context, - delegate: MapSearchDelegate(maps[gamemode.value]!)); - if (sMap != null) { - map.value = sMap; - } - } - : null, - child: Text(map.value ?? "Select"), + SelectMapComponent( + gameMode: gamemode.value, + initialValue: map.value, + onChanged: (p0) { + map.value = p0; + }, ), const SizedBox(height: 8), const Text("Blue Team"), diff --git a/lib/src/screens/event/components/dialogs/edit_fight.dart b/lib/src/screens/event/components/dialogs/edit_fight.dart index 8351b24..0d8120c 100644 --- a/lib/src/screens/event/components/dialogs/edit_fight.dart +++ b/lib/src/screens/event/components/dialogs/edit_fight.dart @@ -21,6 +21,8 @@ import 'package:flutter/material.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:steamwar_multitool/src/components/date_time_editor.dart'; +import 'package:steamwar_multitool/src/components/select_gamemode_component.dart'; +import 'package:steamwar_multitool/src/components/select_map_component.dart'; import 'package:steamwar_multitool/src/provider/provider.dart'; import 'package:steamwar_multitool/src/screens/event/components/team_selector.dart'; import 'package:steamwar_multitool/src/delegates/delegates.dart'; @@ -79,32 +81,25 @@ class EditEventFightDialog extends HookConsumerWidget { const SizedBox(height: 8), const Text("Game Mode"), const SizedBox(height: 8), - ElevatedButton( - onPressed: () async { - final modes = await ref.read(fightServersProvider.future); - final out = await showSearch( - context: context, delegate: GamemodeSearchDelegate(modes)); - if (out != null) { - mode.value = out; + SelectGameModeComponent( + initialValue: mode.value, + onChanged: (p0) { + if (p0 != null) { + mode.value = p0; } }, - child: Text(mode.value), ), const SizedBox(height: 8), const Text("Map"), const SizedBox(height: 8), - ElevatedButton( - onPressed: () async { - final maps = await ref.read(mapsProvider.future); - final out = await showSearch( - context: context, - delegate: MapSearchDelegate(maps[mode.value] ?? [])); - if (out != null) { - map.value = out; - fightsRefresher(); + SelectMapComponent( + gameMode: mode.value, + initialValue: map.value, + onChanged: (p0) { + if (p0 != null) { + map.value = p0; } }, - child: Text(map.value), ), const SizedBox(height: 8), const Text("Kampfleiter"), @@ -180,7 +175,7 @@ class EditEventFightDialog extends HookConsumerWidget { Navigator.of(context).pop(); }, child: const Text("Cancel")), - ElevatedButton( + FilledButton( onPressed: () async { var nav = Navigator.of(context); final repo = await ref.read(eventRepositoryProvider.future); diff --git a/lib/src/screens/event/components/loaded_event.dart b/lib/src/screens/event/components/loaded_event.dart index 08ae2fb..e76359f 100644 --- a/lib/src/screens/event/components/loaded_event.dart +++ b/lib/src/screens/event/components/loaded_event.dart @@ -55,11 +55,11 @@ class LoadedEventScreen extends HookConsumerWidget { final spectateSystemState = useState(event.spectateSystem); final tabController = useTabController(initialLength: 3); - useMemoized(() { - ref.read(schematicTypesProvider.future).then((value) { - schematicTypeState.value = catchToNull( - () => value.firstWhere((element) => element.db == event.schemType)); - }); + useMemoized(() async { + schematicTypeState.value = await ref + .read(schematicTypesProvider.future) + .then((value) => catchToNull(() => + value.firstWhere((element) => element.db == event.schemType))); }); final changed = useState(false); @@ -184,7 +184,6 @@ class LoadedEventScreen extends HookConsumerWidget { ), const SizedBox(height: 8), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text("Deadline: "), DateTimeEditor((p0) { @@ -195,7 +194,6 @@ class LoadedEventScreen extends HookConsumerWidget { ), const SizedBox(height: 8), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text("Start: "), DateTimeEditor((p0) { @@ -206,7 +204,6 @@ class LoadedEventScreen extends HookConsumerWidget { ), const SizedBox(height: 8), Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ const Text("End: "), DateTimeEditor((p0) { @@ -264,10 +261,17 @@ class LoadedEventScreen extends HookConsumerWidget { ], ), const SizedBox(height: 8), - ref.watch(schematicTypesProvider).when( + ref.read(schematicTypesProvider).when( data: (data) { return DropdownMenu( dropdownMenuEntries: [ + const DropdownMenuEntry(value: null, label: ""), + if (schematicTypeState.value != null && + !data.contains(schematicTypeState.value)) + DropdownMenuEntry( + value: schematicTypeState.value!, + label: schematicTypeState.value!.name, + ), for (final type in data) DropdownMenuEntry( value: type, @@ -276,21 +280,21 @@ class LoadedEventScreen extends HookConsumerWidget { ], initialSelection: schematicTypeState.value, enableSearch: true, - width: 300, + enableFilter: true, + width: 200, menuHeight: 300, label: const Text("Schematic Type"), - onSelected: (value) { - schematicTypeState.value = value; + onSelected: (v) async { + schematicTypeState.value = v; changed.value = true; }, - enableFilter: true, ); }, error: (err, stk) { return ErrorComponent(err, stk); }, loading: () { - return LinearProgressIndicator(); + return const Disabled(initialValue: "Schematic Type"); }, ), const SizedBox(height: 8), diff --git a/lib/src/screens/generator/generators/group_generator.dart b/lib/src/screens/generator/generators/group_generator.dart index 019ad36..51b9622 100644 --- a/lib/src/screens/generator/generators/group_generator.dart +++ b/lib/src/screens/generator/generators/group_generator.dart @@ -24,6 +24,7 @@ import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:go_router/go_router.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; import 'package:steamwar_multitool/src/components/components.dart'; +import 'package:steamwar_multitool/src/components/select_map_component.dart'; import 'package:steamwar_multitool/src/delegates/delegates.dart'; import 'package:steamwar_multitool/src/provider/data.dart'; import 'package:steamwar_multitool/src/provider/events.dart'; @@ -158,7 +159,7 @@ class GroupBracketGenerator extends HookConsumerWidget { }, ), ], - leading: BackButton(), + leading: const BackButton(), ), floatingActionButton: FloatingActionButton.extended( icon: const Icon(Icons.check), @@ -307,7 +308,7 @@ class GroupBracketGenerator extends HookConsumerWidget { ), ], ), - Divider(), + const Divider(), Column( children: [ Row( @@ -352,20 +353,11 @@ class GroupBracketGenerator extends HookConsumerWidget { ), Row( children: [ - const Text('Map:'), - TextButton( - onPressed: gamemode.value != null - ? () async { - final newmap = await showSearch( - context: context, - delegate: MapSearchDelegate(await ref - .read(mapsProvider.future) - .then((value) => - value[gamemode.value]!))); - map.value = newmap; - } - : null, - child: Text(map.value ?? 'None')), + SelectMapComponent( + gameMode: gamemode.value, + initialValue: map.value, + onChanged: (p0) => map.value = p0, + ) ], ), const Divider(),