From 6b8f631235d70bc0d13d688fa74451d606c50872 Mon Sep 17 00:00:00 2001 From: Chaoscaot Date: Sat, 28 Jan 2023 17:06:35 +0100 Subject: [PATCH] Fix Display and generator starts --- lib/src/app.dart | 5 +- lib/src/screens/display/display.dart | 87 +++++--- lib/src/screens/event/bracket_generator.dart | 30 --- .../screens/event/components/fight_list.dart | 44 ++++ .../screens/generator/bracket_generator.dart | 62 ++++++ .../generator/generators/group_generator.dart | 199 ++++++++++++++++++ .../screens/generator/loaded_generator.dart | 61 ++++++ lib/src/screens/home/home.dart | 1 + 8 files changed, 432 insertions(+), 57 deletions(-) delete mode 100644 lib/src/screens/event/bracket_generator.dart create mode 100644 lib/src/screens/generator/bracket_generator.dart create mode 100644 lib/src/screens/generator/generators/group_generator.dart create mode 100644 lib/src/screens/generator/loaded_generator.dart diff --git a/lib/src/app.dart b/lib/src/app.dart index de2ad7d..1d57c80 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -21,6 +21,7 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:steamwar_multitool/src/screens/display/display.dart'; +import 'package:steamwar_multitool/src/screens/generator/bracket_generator.dart'; import 'package:steamwar_multitool/src/screens/event/event.dart'; import 'package:steamwar_multitool/src/screens/event/event_fights_graph.dart'; @@ -46,14 +47,14 @@ final _routes = GoRouter( return EditEventScreen(eventId); }), GoRoute( - path: "/event/:eventId/graph", + path: "/event/:eventId/generator", builder: (context, state) { final eventId = int.tryParse(state.params['eventId']!); if (eventId == null) { context.go("/"); return const SizedBox(); } - return EventfightsGraph(eventId); + return BracketGeneratorScreen(eventId); }), GoRoute( path: "/display/:eventId", diff --git a/lib/src/screens/display/display.dart b/lib/src/screens/display/display.dart index 76fa3a9..58ab4a2 100644 --- a/lib/src/screens/display/display.dart +++ b/lib/src/screens/display/display.dart @@ -78,16 +78,27 @@ class DisplayScreenLoaded extends HookConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { + final fights = useState( + eventData.fights.where((element) => element.group == group).toList()); final nextFight = useMemoized(() { - final fights = eventData.fights; - if (fights.isEmpty) { + Future.delayed(const Duration(seconds: 30), () async { + fights.value = (await (await ref.read(eventRepositoryProvider.future)) + .listFights(eventData.event.id)) + .where((element) => element.group == group) + .toList(); + }); + if (fights.value.isEmpty) { return null; } final now = DateTime.now(); - final nextFight = fights.firstWhere( - (element) => element.start.isAfter(now) && element.group == group); - return nextFight; - }); + try { + final nextFight = fights.value.firstWhere( + (element) => element.start.isAfter(now) && element.group == group); + return nextFight; + } catch (e) { + return null; + } + }, [fights.value]); return Theme( data: ThemeData.dark(useMaterial3: true), @@ -95,12 +106,6 @@ class DisplayScreenLoaded extends HookConsumerWidget { backgroundColor: Colors.blue, body: Stack( children: [ - Image.asset( - "images/background.png", - fit: BoxFit.cover, - height: double.infinity, - width: double.infinity, - ), Column( mainAxisSize: MainAxisSize.max, children: [ @@ -108,22 +113,35 @@ class DisplayScreenLoaded extends HookConsumerWidget { SizedBox( width: double.infinity, child: Card( - margin: const EdgeInsets.all(8), - child: Column( - children: [ - Padding( - padding: const EdgeInsets.all(8.0), - child: Center( - child: Text( - 'SteamWar.de - ${eventData.event.name}', - style: const TextStyle(fontSize: 24))), + margin: const EdgeInsets.all(8), + child: Column( + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Row( + children: [ + Flexible( + child: ChipClock(), + flex: 1, + ), + Spacer( + flex: 4, + ), + Text('SteamWar.de - ${eventData.event.name}', + style: const TextStyle(fontSize: 24)), + Spacer( + flex: 5, + ), + ], ), - ], - )), + ), + ], + ), + ), ), Expanded( child: FightList( - eventData.fights + fights.value .where( (element) => group == null || element.group == group) .toList(), @@ -154,6 +172,25 @@ class DisplayScreenLoaded extends HookConsumerWidget { } } +class ChipClock extends HookConsumerWidget { + const ChipClock({ + super.key, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final update = useState(0); + useEffect(() { + Future.delayed(const Duration(seconds: 10), () { + update.value++; + }); + }, [update.value]); + return Chip( + label: Text(kTimeFormat.format(DateTime.now())), + ); + } +} + final kTimeFormat = DateFormat('HH:mm'); class FightList extends HookConsumerWidget { @@ -171,7 +208,7 @@ class FightList extends HookConsumerWidget { scrollController.offset) { npos = 0.0; } else { - npos = scrollController.offset + 300; + npos = scrollController.offset + 325; } await scrollController.animateTo( npos, diff --git a/lib/src/screens/event/bracket_generator.dart b/lib/src/screens/event/bracket_generator.dart deleted file mode 100644 index ac7a508..0000000 --- a/lib/src/screens/event/bracket_generator.dart +++ /dev/null @@ -1,30 +0,0 @@ -/* - * 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'; - -class BracketGeneratorScreen extends HookConsumerWidget { - const BracketGeneratorScreen({Key? key}) : super(key: key); - - @override - Widget build(BuildContext context, WidgetRef ref) { - return const Placeholder(); - } -} diff --git a/lib/src/screens/event/components/fight_list.dart b/lib/src/screens/event/components/fight_list.dart index ac4b33c..a10adaf 100644 --- a/lib/src/screens/event/components/fight_list.dart +++ b/lib/src/screens/event/components/fight_list.dart @@ -20,6 +20,7 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_hooks/flutter_hooks.dart'; import 'package:grouped_list/grouped_list.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -308,11 +309,54 @@ class EventFightList extends HookConsumerWidget { value: "export", child: Text("Export Data"), ), + PopupMenuItem( + child: Text("Display Link"), + value: "display", + ), ]; }, onSelected: (value) { if (value == "export") { openExportedFights(fights.value, event.name); + } else if (value == "display") { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text("Display Link"), + content: Text( + "https://steamwar.de/eventplanner/#/display/${event.id}", + style: const TextStyle(fontFamily: "monospace"), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text("Close"), + ), + ElevatedButton( + onPressed: () async { + Clipboard.setData(ClipboardData( + text: + "https://steamwar.de/eventplanner/#/display/${event.id}?key=${(await ref.read(userDataProvider.future)).key}")); + Navigator.of(context).pop(); + }, + child: const Text("Copy With Key"), + ), + ElevatedButton( + onPressed: () { + Clipboard.setData(ClipboardData( + text: + "https://steamwar.de/eventplanner/#/display/${event.id}")); + Navigator.of(context).pop(); + }, + child: const Text("Copy"), + ), + ], + ); + }, + ); } }, ), diff --git a/lib/src/screens/generator/bracket_generator.dart b/lib/src/screens/generator/bracket_generator.dart new file mode 100644 index 0000000..e48d263 --- /dev/null +++ b/lib/src/screens/generator/bracket_generator.dart @@ -0,0 +1,62 @@ +/* + * 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: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/provider/events.dart'; +import 'package:steamwar_multitool/src/screens/generator/loaded_generator.dart'; +import 'package:steamwar_multitool/src/types/types.dart'; + +class BracketGeneratorScreen extends HookConsumerWidget { + final int eventId; + const BracketGeneratorScreen(this.eventId, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final event = useMemoized(() => ref + .watch(eventRepositoryProvider.future) + .then((value) => value.getEvent(eventId))); + + return FutureBuilder( + builder: (context, data) { + if (data.hasError) { + return Scaffold( + appBar: AppBar( + title: const Text('Error'), + ), + body: ErrorComponent(data.error!, data.stackTrace)); + } + + if (!data.hasData) { + return Scaffold( + appBar: AppBar( + title: const Text('Loading'), + ), + body: const LinearProgressIndicator()); + } + + final event = data.data as EventExtended; + return LoadedBracketGenerator(event); + }, + future: event, + ); + } +} diff --git a/lib/src/screens/generator/generators/group_generator.dart b/lib/src/screens/generator/generators/group_generator.dart new file mode 100644 index 0000000..f4ced38 --- /dev/null +++ b/lib/src/screens/generator/generators/group_generator.dart @@ -0,0 +1,199 @@ +/* + * 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:flutter_hooks/flutter_hooks.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/types/types.dart'; + +class GroupBracketGenerator extends HookConsumerWidget { + final EventExtended event; + + const GroupBracketGenerator(this.event, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final groups = useState>>([]); + + final notInGroupTeams = useMemoized( + () => event.teams + .where((element) => + !groups.value.any((element2) => element2.contains(element.id))) + .toList(), + [groups.value]); + + useMemoized(() { + for (final group in groups.value) { + if (group.isEmpty) { + groups.value = + groups.value.where((element) => element != group).toList(); + } + } + }, [groups.value]); + + final canProceed = useMemoized( + () => + groups.value.every((element) => element.length >= 2) && + groups.value.reduce((value, element) => value + element).length == + event.teams.length, + [groups.value, notInGroupTeams]); + + return Scaffold( + appBar: AppBar( + title: Text('Group Bracket Generator'), + ), + floatingActionButton: FloatingActionButton( + onPressed: !canProceed + ? null + : () { + groups.value.add([]); + }, + child: Icon(Icons.navigate_next), + ), + body: Column( + mainAxisSize: MainAxisSize.max, + children: [ + Wrap( + crossAxisAlignment: WrapCrossAlignment.start, + alignment: WrapAlignment.start, + children: [ + for (final team in notInGroupTeams) + Padding( + padding: const EdgeInsets.all(8.0), + child: Draggable( + data: team, + feedback: Material( + color: Colors.transparent, child: _TeamChip(team)), + child: _TeamChip(team), + ), + ), + ], + ), + Wrap( + crossAxisAlignment: WrapCrossAlignment.start, + alignment: WrapAlignment.start, + children: [ + for (final group in groups.value) + Padding( + padding: const EdgeInsets.all(8.0), + child: DragTarget( + builder: (context, candidateData, rejectedData) { + return SizedBox( + width: 200, + height: 200, + child: Card( + child: Column( + children: [ + Text('Group ${groups.value.indexOf(group) + 1}'), + Wrap( + children: [ + for (final teamId in group) + Padding( + padding: const EdgeInsets.all(8.0), + child: InkWell( + child: _TeamChip( + event.teams.firstWhere((element) => + element.id == teamId), + ), + onTap: () { + groups.value = [ + for (final addGroup in groups.value) + if (addGroup.contains(teamId)) + addGroup + .where((element) => + element != teamId) + .toList() + else + addGroup + ]; + }, + ), + ), + ], + ), + ], + ), + ), + ); + }, + onWillAccept: (data) => true, + onAccept: (data) { + groups.value = [ + for (final addGroup in groups.value) + if (addGroup.contains(data.id)) + addGroup + .where((element) => element != data.id) + .toList() + else if (addGroup == group) + [...addGroup, data.id] + else + addGroup + ]; + }, + ), + ), + Padding( + padding: const EdgeInsets.all(8.0), + child: DragTarget( + builder: (context, candidateData, rejectedData) { + return SizedBox( + width: 200, + height: 200, + child: Card( + child: Column( + children: const [ + Text('New Group'), + ], + ), + ), + ); + }, + onWillAccept: (data) => true, + onAccept: (data) { + groups.value = [ + ...groups.value, + [data.id], + ]; + }, + ), + ), + ], + ), + ], + ), + ); + } +} + +class _TeamChip extends StatelessWidget { + final Team team; + const _TeamChip(this.team, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context) { + return Chip( + label: Text(team.name, + style: TextStyle( + color: team.color.computeLuminance() > 0.5 + ? Colors.black + : Colors.white)), + backgroundColor: team.color, + ); + } +} diff --git a/lib/src/screens/generator/loaded_generator.dart b/lib/src/screens/generator/loaded_generator.dart new file mode 100644 index 0000000..23c57ea --- /dev/null +++ b/lib/src/screens/generator/loaded_generator.dart @@ -0,0 +1,61 @@ +/* + * 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:go_router/go_router.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:steamwar_multitool/src/screens/generator/generators/group_generator.dart'; +import 'package:steamwar_multitool/src/types/event.dart'; + +class BracketGenerators { + String name; + Widget Function(EventExtended event) builder; + + BracketGenerators(this.name, this.builder); +} + +final List bracketGenerators = [ + BracketGenerators('Group Bracket', (event) => GroupBracketGenerator(event)), +]; + +class LoadedBracketGenerator extends HookConsumerWidget { + final EventExtended event; + const LoadedBracketGenerator(this.event, {Key? key}) : super(key: key); + + @override + Widget build(BuildContext context, WidgetRef ref) { + return Scaffold( + appBar: AppBar( + title: Text('Bracket Generator'), + leading: + BackButton(onPressed: () => context.go('/event/${event.event.id}')), + ), + body: ListView( + children: [ + for (final generator in bracketGenerators) + ListTile( + title: Text(generator.name), + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => generator.builder(event))), + ), + ], + ), + ); + } +} diff --git a/lib/src/screens/home/home.dart b/lib/src/screens/home/home.dart index df5cc1e..7902dd2 100644 --- a/lib/src/screens/home/home.dart +++ b/lib/src/screens/home/home.dart @@ -60,6 +60,7 @@ class HomeScreen extends HookConsumerWidget { return; } navRailIndex.value = index; + Navigator.pop(context); }, children: [ Padding(