1
0

Update Edit Event State Backend

Dieser Commit ist enthalten in:
Chaoscaot 2023-04-15 11:30:37 +02:00
Ursprung 89d1e1d5b5
Commit a2649c6a28
7 geänderte Dateien mit 341 neuen und 228 gelöschten Zeilen

Datei anzeigen

@ -31,3 +31,10 @@ final eventsListProvider = FutureProvider<List<ShortEvent>>((ref) async {
final repo = await ref.watch(eventRepositoryProvider.future);
return repo.listEvents();
}, dependencies: [eventRepositoryProvider]);
final eventFutureProvider =
FutureProvider.family.autoDispose<EventExtended, int>((ref, id) async {
return ref
.watch(eventRepositoryProvider.future)
.then((value) => value.getEvent(id));
});

Datei anzeigen

@ -26,57 +26,24 @@ 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/provider/provider.dart';
import 'package:steamwar_multitool/src/screens/event/components/fight_list.dart';
import 'package:steamwar_multitool/src/screens/event/components/team_list.dart';
import 'package:steamwar_multitool/src/screens/event/components/tabs/edit_event.dart';
import 'package:steamwar_multitool/src/screens/event/components/tabs/fight_list.dart';
import 'package:steamwar_multitool/src/screens/event/components/tabs/team_list.dart';
import 'package:steamwar_multitool/src/screens/event/event.dart';
import 'package:steamwar_multitool/src/delegates/delegates.dart';
import 'package:steamwar_multitool/src/screens/event/state/event.dart';
import 'package:steamwar_multitool/src/types/types.dart';
class LoadedEventScreen extends HookConsumerWidget {
const LoadedEventScreen({Key? key, required this.eventData})
: super(key: key);
final EventExtended eventData;
const LoadedEventScreen({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final eventData = ref.watch(eventProvider);
final event = eventData.event;
final nameController = useTextEditingController(text: event.name);
final deadlineState = useState(event.deadline);
final startDateState = useState(event.start);
final endDateState = useState(event.end);
final maxTeamMembersState = useState(event.maxTeamMembers);
final maxTeamMembersController =
useTextEditingController(text: event.maxTeamMembers.toString());
final invalidMaxTeamMembers = useState(false);
final schematicTypeState = useState<SchematicType?>(null);
final publicOnlyState = useState(event.publicSchemsOnly);
final spectateSystemState = useState(event.spectateSystem);
final tabController = useTabController(initialLength: 3);
useMemoized(() async {
schematicTypeState.value = await ref
.read(schematicTypesProvider.future)
.then((value) => catchToNull(() =>
value.firstWhere((element) => element.db == event.schemType)));
});
final fightCount = useMemoized(() {
Map<int, int> fightCount = {};
for (var team in eventData.teams) {
fightCount[team.id] = 0;
}
for (var fight in eventData.fights) {
fightCount[fight.blueTeam.id] =
(fightCount[fight.blueTeam.id] ?? 0) + 1;
fightCount[fight.redTeam.id] = (fightCount[fight.redTeam.id] ?? 0) + 1;
}
return fightCount;
}, [eventData.fights, eventData.teams]);
final changed = useState(false);
return Scaffold(
appBar: AppBar(
title: Text("${"Edit"} ${event.name}"),
@ -113,20 +80,20 @@ class LoadedEventScreen extends HookConsumerWidget {
icon: const Icon(Icons.delete_outline, color: Colors.red),
),
IconButton(
onPressed: changed.value
onPressed: ref.watch(changedProvider)
? () {
ref
.read(eventRepositoryProvider.future)
.then((value) => value.updateEvent(
event.id,
nameController.text,
deadlineState.value,
startDateState.value,
endDateState.value,
maxTeamMembersState.value,
schematicTypeState.value?.db,
publicOnlyState.value,
spectateSystemState.value,
ref.read(eventNameProvider),
ref.read(eventDeadlineProvider),
ref.read(eventStartProvider),
ref.read(eventEndProvider),
ref.read(maxTeamSizeProvider),
ref.read(schematicTypeProvider),
ref.read(publicOnlyProvider),
ref.read(spectateSystemProvider),
))
.whenComplete(
() => ref.invalidate(eventsListProvider));
@ -138,7 +105,7 @@ class LoadedEventScreen extends HookConsumerWidget {
leading: BackButton(
onPressed: () async {
var canPop = false;
if (changed.value) {
if (ref.read(changedProvider)) {
final accepted = await showDialog(
context: context,
builder: (context) => AlertDialog(
@ -181,171 +148,10 @@ class LoadedEventScreen extends HookConsumerWidget {
padding: const EdgeInsets.all(8.0),
child: TabBarView(
controller: tabController,
children: [
ListView(
shrinkWrap: true,
children: [
const SizedBox(height: 8),
TextField(
controller: nameController,
decoration: const InputDecoration(
labelText: "Name",
border: OutlineInputBorder(),
),
onChanged: (value) {
changed.value = true;
},
),
const SizedBox(height: 8),
Wrap(
alignment: WrapAlignment.spaceBetween,
children: [
Column(
children: [
const Tooltip(
message: "Deadline",
child: Icon(Icons.block),
),
DateTimeEditor((p0) {
deadlineState.value = p0;
changed.value = true;
}, deadlineState.value, true),
],
),
Column(
children: [
const Tooltip(
message: "Start",
child: Icon(Icons.swipe_right_alt),
),
DateTimeEditor((p0) {
startDateState.value = p0;
changed.value = true;
}, startDateState.value, true),
],
),
Column(
children: [
const Tooltip(
message: "End",
child: Icon(Icons.swipe_left_alt),
),
DateTimeEditor((p0) {
endDateState.value = p0;
changed.value = true;
}, endDateState.value, true),
],
),
],
),
const SizedBox(height: 16),
Row(
children: [
SizedBox(
width: 150,
child: TextField(
controller: maxTeamMembersController,
decoration: InputDecoration(
labelText: "Max Team Members",
border: const OutlineInputBorder(),
errorText: invalidMaxTeamMembers.value
? "Must be a number above 0"
: null),
keyboardType: TextInputType.number,
onChanged: (value) {
if (value.isEmpty ||
int.tryParse(value) == null ||
int.parse(value) <= 0) {
invalidMaxTeamMembers.value = true;
} else {
invalidMaxTeamMembers.value = false;
maxTeamMembersState.value = int.parse(value);
changed.value = true;
}
},
),
),
Expanded(
child: Slider(
value: maxTeamMembersState.value.toDouble(),
onChanged: (p0) {
maxTeamMembersState.value = p0.toInt();
maxTeamMembersController.text =
p0.toInt().toString();
changed.value = true;
},
min: min(1, maxTeamMembersState.value.toDouble()),
max:
max(30, maxTeamMembersState.value.toDouble()),
divisions:
max(30, maxTeamMembersState.value.toDouble())
.toInt() -
1,
label: maxTeamMembersState.value.toString(),
),
),
],
),
const SizedBox(height: 8),
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,
label: type.name,
)
],
initialSelection: schematicTypeState.value,
enableSearch: true,
enableFilter: true,
width: 200,
menuHeight: 300,
label: const Text("Schematic Type"),
onSelected: (v) async {
schematicTypeState.value = v;
changed.value = true;
},
);
},
error: (err, stk) {
return ErrorComponent(err, stk);
},
loading: () {
return const Disabled(initialValue: "Schematic Type");
},
),
const SizedBox(height: 8),
SwitchListTile(
value: publicOnlyState.value,
onChanged: (value) {
publicOnlyState.value = value;
changed.value = true;
},
title: const Text("Public Only"),
),
SwitchListTile(
value: spectateSystemState.value,
onChanged: (value) {
spectateSystemState.value = value;
changed.value = true;
},
title: const Text("Use Spectate System"),
),
],
),
TeamList(eventData.teams, fightCount),
EventFightList(
eventData: eventData,
),
children: const [
EditEventTab(),
TeamList(),
EventFightList(),
],
),
),

Datei anzeigen

@ -0,0 +1,197 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import 'dart:math';
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/data.dart';
import 'package:steamwar_multitool/src/screens/event/event.dart';
import 'package:steamwar_multitool/src/screens/event/state/event.dart';
import 'package:steamwar_multitool/src/types/schematic.dart';
class EditEventTab extends HookConsumerWidget {
const EditEventTab({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final nameTextController =
useTextEditingController(text: ref.watch(eventNameProvider));
final maxTeamMembersController = useTextEditingController(
text: ref.watch(maxTeamSizeProvider).toString());
final invalidMaxTeamMembers = useState(false);
return ListView(
shrinkWrap: true,
children: [
const SizedBox(height: 8),
TextField(
controller: nameTextController,
decoration: const InputDecoration(
labelText: "Name",
border: OutlineInputBorder(),
),
onChanged: (value) {
ref.read(eventNameProvider.notifier).state = value;
},
),
const SizedBox(height: 8),
Wrap(
alignment: WrapAlignment.spaceBetween,
children: [
Column(
children: [
const Tooltip(
message: "Deadline",
child: Icon(Icons.block),
),
DateTimeEditor(
(p0) {
ref.read(eventDeadlineProvider.notifier).state = p0;
},
ref.watch(eventDeadlineProvider),
true,
),
],
),
Column(
children: [
const Tooltip(
message: "Start",
child: Icon(Icons.swipe_right_alt),
),
DateTimeEditor((p0) {
ref.read(eventStartProvider.notifier).state = p0;
}, ref.watch(eventStartProvider), true),
],
),
Column(
children: [
const Tooltip(
message: "End",
child: Icon(Icons.swipe_left_alt),
),
DateTimeEditor((p0) {
ref.read(eventEndProvider.notifier).state = p0;
}, ref.watch(eventEndProvider), true),
],
),
],
),
const SizedBox(height: 16),
Row(
children: [
SizedBox(
width: 150,
child: TextField(
controller: maxTeamMembersController,
decoration: InputDecoration(
labelText: "Max Team Members",
border: const OutlineInputBorder(),
errorText: invalidMaxTeamMembers.value
? "Must be a number above 0"
: null),
keyboardType: TextInputType.number,
onChanged: (value) {
if (value.isEmpty ||
int.tryParse(value) == null ||
int.parse(value) <= 0) {
invalidMaxTeamMembers.value = true;
} else {
invalidMaxTeamMembers.value = false;
ref.read(maxTeamSizeProvider.notifier).state =
int.parse(value);
}
},
),
),
Expanded(
child: Slider(
value: ref.read(maxTeamSizeProvider).toDouble(),
onChanged: (p0) {
ref.read(maxTeamSizeProvider.notifier).state = p0.toInt();
maxTeamMembersController.text = p0.toInt().toString();
},
min: min(1, ref.read(maxTeamSizeProvider).toDouble()),
max: max(30, ref.read(maxTeamSizeProvider).toDouble()),
divisions:
max(30, ref.read(maxTeamSizeProvider).toDouble()).toInt() -
1,
label: ref.read(maxTeamSizeProvider).toString(),
),
),
],
),
const SizedBox(height: 8),
ref.watch(schematicTypesProvider).when(
data: (data) {
final schematicTypeState = ref.watch(schematicTypeProvider);
return DropdownMenu<SchematicType?>(
dropdownMenuEntries: [
const DropdownMenuEntry(value: null, label: ""),
for (final type in data)
DropdownMenuEntry(
value: type,
label: type.name,
)
],
initialSelection: catchToNull(() => data
.firstWhere((element) => element.db == schematicTypeState)),
enableSearch: true,
enableFilter: true,
width: 200,
menuHeight: 300,
label: const Text("Schematic Type"),
onSelected: (v) async {
ref.read(schematicTypeProvider.notifier).state = v?.db;
},
);
},
error: (err, stk) {
return ErrorComponent(err, stk);
},
loading: () {
return const Disabled(initialValue: "Schematic Type");
},
),
const SizedBox(height: 8),
SwitchListTile(
value: ref.watch(publicOnlyProvider),
onChanged: (value) {
ref.read(publicOnlyProvider.notifier).state = value;
},
title: const Text("Public Only"),
),
SwitchListTile(
value: ref.watch(spectateSystemProvider),
onChanged: (value) {
ref.read(spectateSystemProvider.notifier).state = value;
},
title: const Text("Use Spectate System"),
),
],
);
}
}

Datei anzeigen

@ -29,6 +29,7 @@ import 'package:steamwar_multitool/src/components/date_time_editor.dart';
import 'package:steamwar_multitool/src/provider/provider.dart';
import 'package:steamwar_multitool/src/delegates/delegates.dart';
import 'package:steamwar_multitool/src/screens/event/components/dialogs/dialogs.dart';
import 'package:steamwar_multitool/src/screens/event/state/event.dart';
import 'package:steamwar_multitool/src/screens/generator/bracket_generator.dart';
import 'package:steamwar_multitool/src/screens/generator/generators/group_generator.dart';
import 'package:steamwar_multitool/src/types/types.dart';
@ -36,12 +37,11 @@ import 'package:steamwar_multitool/src/util/constants.dart';
import 'package:url_launcher/url_launcher.dart';
class EventFightList extends HookConsumerWidget {
final EventExtended eventData;
const EventFightList({Key? key, required this.eventData}) : super(key: key);
const EventFightList({Key? key}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final eventData = ref.watch(eventProvider);
final event = eventData.event;
final fights = useState<List<EventFight>>(eventData.fights);
final selected = useState<List<int>>([]);

Datei anzeigen

@ -19,20 +19,19 @@
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:steamwar_multitool/src/screens/event/state/event.dart';
import 'package:steamwar_multitool/src/types/types.dart';
class TeamList extends HookConsumerWidget {
final List<Team> teams;
final Map<int, int> fightCount;
const TeamList(
this.teams,
this.fightCount, {
const TeamList({
Key? key,
}) : super(key: key);
@override
Widget build(BuildContext context, WidgetRef ref) {
final teams = ref.watch(eventProvider).teams;
final fightCount = ref.watch(eventFightsProvider);
return ListView(
children: [
ListTile(

Datei anzeigen

@ -22,6 +22,7 @@ import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:steamwar_multitool/src/provider/events.dart';
import 'package:steamwar_multitool/src/screens/event/components/loaded_event.dart';
import 'package:steamwar_multitool/src/screens/event/state/event.dart';
import 'package:steamwar_multitool/src/types/types.dart';
T? catchToNull<T>(T Function() f) {
@ -38,9 +39,7 @@ class EditEventScreen extends HookConsumerWidget {
@override
Widget build(BuildContext context, WidgetRef ref) {
final eventFuture = useMemoized(() => ref
.watch(eventRepositoryProvider.future)
.then((value) => value.getEvent(eventId)));
final eventFuture = ref.watch(eventFutureProvider(eventId).future);
return FutureBuilder(
future: eventFuture,
@ -65,8 +64,21 @@ class EditEventScreen extends HookConsumerWidget {
}
final eventData = snapshot.data as EventExtended;
return LoadedEventScreen(
eventData: eventData,
return ProviderScope(
overrides: [
eventProvider.overrideWithValue(eventData),
eventNameProvider,
eventDeadlineProvider,
eventStartProvider,
eventEndProvider,
maxTeamSizeProvider,
schematicTypeProvider,
publicOnlyProvider,
spectateSystemProvider,
changedProvider,
eventFightsProvider,
],
child: const LoadedEventScreen(),
);
},
);

Datei anzeigen

@ -0,0 +1,92 @@
/*
* 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 <https://www.gnu.org/licenses/>.
*/
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:steamwar_multitool/src/provider/provider.dart';
import 'package:steamwar_multitool/src/screens/event/event.dart';
import 'package:steamwar_multitool/src/types/types.dart';
final eventProvider = Provider<EventExtended>((ref) {
throw UnimplementedError();
});
final eventNameProvider = StateProvider<String>((ref) {
return ref.read(eventProvider).event.name;
});
final eventDeadlineProvider = StateProvider<DateTime>((ref) {
return ref.read(eventProvider).event.deadline;
});
final eventStartProvider = StateProvider<DateTime>((ref) {
return ref.read(eventProvider).event.start;
});
final eventEndProvider = StateProvider<DateTime>((ref) {
return ref.read(eventProvider).event.end;
});
final maxTeamSizeProvider = StateProvider<int>((ref) {
return ref.read(eventProvider).event.maxTeamMembers;
});
final schematicTypeProvider = StateProvider<String?>((ref) {
return ref.read(eventProvider).event.schemType;
});
final schematicTypeFutureProvider = FutureProvider<SchematicType?>((ref) async {
final types = await ref.read(schematicTypesProvider.future);
final type = catchToNull(() => types
.firstWhere((element) => element.db == ref.watch(schematicTypeProvider)));
return type;
});
final publicOnlyProvider = StateProvider<bool>((ref) {
return ref.read(eventProvider).event.publicSchemsOnly;
});
final spectateSystemProvider = StateProvider<bool>((ref) {
return ref.read(eventProvider).event.spectateSystem;
});
final changedProvider = Provider<bool>((ref) {
final event = ref.watch(eventProvider);
return event.event.name != ref.watch(eventNameProvider) ||
event.event.deadline != ref.watch(eventDeadlineProvider) ||
event.event.start != ref.watch(eventStartProvider) ||
event.event.end != ref.watch(eventEndProvider) ||
event.event.maxTeamMembers != ref.watch(maxTeamSizeProvider) ||
event.event.schemType != ref.watch(schematicTypeProvider) ||
event.event.publicSchemsOnly != ref.watch(publicOnlyProvider) ||
event.event.spectateSystem != ref.watch(spectateSystemProvider);
});
final eventFightsProvider = Provider<Map<int, int>>((ref) {
final eventData = ref.watch(eventProvider);
Map<int, int> fightCount = {};
for (var team in eventData.teams) {
fightCount[team.id] = 0;
}
for (var fight in eventData.fights) {
fightCount[fight.blueTeam.id] = (fightCount[fight.blueTeam.id] ?? 0) + 1;
fightCount[fight.redTeam.id] = (fightCount[fight.redTeam.id] ?? 0) + 1;
}
return fightCount;
});