Add Group Bracket Generator
Dieser Commit ist enthalten in:
Ursprung
6b8f631235
Commit
1a515603b0
@ -22,6 +22,7 @@ import 'dart:math';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:grouped_list/grouped_list.dart';
|
import 'package:grouped_list/grouped_list.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
import 'package:steamwar_multitool/src/components/date_time_editor.dart';
|
||||||
@ -204,7 +205,7 @@ class EventFightList extends HookConsumerWidget {
|
|||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.person)),
|
icon: const Icon(Icons.gavel)),
|
||||||
),
|
),
|
||||||
Tooltip(
|
Tooltip(
|
||||||
message: "Change Group",
|
message: "Change Group",
|
||||||
@ -302,6 +303,13 @@ class EventFightList extends HookConsumerWidget {
|
|||||||
icon: const Icon(Icons.add),
|
icon: const Icon(Icons.add),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
|
FilledButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
context.go("/event/${event.id}/generator");
|
||||||
|
},
|
||||||
|
label: const Text("Generate Fight"),
|
||||||
|
icon: const Icon(Icons.shuffle)),
|
||||||
|
const SizedBox(width: 8),
|
||||||
PopupMenuButton(
|
PopupMenuButton(
|
||||||
itemBuilder: (context) {
|
itemBuilder: (context) {
|
||||||
return const [
|
return const [
|
||||||
|
@ -118,7 +118,7 @@ class LoadedEventScreen extends HookConsumerWidget {
|
|||||||
: null,
|
: null,
|
||||||
icon: const Icon(Icons.save)),
|
icon: const Icon(Icons.save)),
|
||||||
],
|
],
|
||||||
leading: IconButton(
|
leading: BackButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
var canPop = false;
|
var canPop = false;
|
||||||
if (changed.value) {
|
if (changed.value) {
|
||||||
@ -147,7 +147,6 @@ class LoadedEventScreen extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
if (canPop) context.go("/");
|
if (canPop) context.go("/");
|
||||||
},
|
},
|
||||||
icon: const Icon(Icons.arrow_back),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: Padding(
|
body: Padding(
|
||||||
@ -283,7 +282,7 @@ class LoadedEventScreen extends HookConsumerWidget {
|
|||||||
side: const BorderSide(color: Colors.transparent),
|
side: const BorderSide(color: Colors.transparent),
|
||||||
label: Text(team.name,
|
label: Text(team.name,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: team.color.computeLuminance() > 0.5
|
color: team.color.computeLuminance() > 0.4
|
||||||
? Colors.black
|
? Colors.black
|
||||||
: Colors.white)),
|
: Colors.white)),
|
||||||
backgroundColor: team.color,
|
backgroundColor: team.color,
|
||||||
|
@ -17,10 +17,18 @@
|
|||||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import 'dart:math';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:steamwar_multitool/src/components/components.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';
|
||||||
import 'package:steamwar_multitool/src/types/types.dart';
|
import 'package:steamwar_multitool/src/types/types.dart';
|
||||||
|
import 'package:steamwar_multitool/src/util/constants.dart';
|
||||||
|
|
||||||
class GroupBracketGenerator extends HookConsumerWidget {
|
class GroupBracketGenerator extends HookConsumerWidget {
|
||||||
final EventExtended event;
|
final EventExtended event;
|
||||||
@ -47,49 +55,233 @@ class GroupBracketGenerator extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
}, [groups.value]);
|
}, [groups.value]);
|
||||||
|
|
||||||
|
Team getTeam(int id) =>
|
||||||
|
event.teams.firstWhere((element) => element.id == id);
|
||||||
|
|
||||||
|
final startTime = useState(event.event.start);
|
||||||
|
final roundTime = useState(const Duration(minutes: 30));
|
||||||
|
final startDelay = useState(const Duration(seconds: 30));
|
||||||
|
|
||||||
|
final gamemode = useState<String?>(null);
|
||||||
|
final map = useState<String?>(null);
|
||||||
|
|
||||||
final canProceed = useMemoized(
|
final canProceed = useMemoized(
|
||||||
() =>
|
() =>
|
||||||
groups.value.every((element) => element.length >= 2) &&
|
groups.value.every((element) => element.length >= 2) &&
|
||||||
groups.value.reduce((value, element) => value + element).length ==
|
groups.value.isNotEmpty &&
|
||||||
event.teams.length,
|
gamemode.value != null &&
|
||||||
[groups.value, notInGroupTeams]);
|
map.value != null,
|
||||||
|
[groups.value, notInGroupTeams, gamemode.value, map.value]);
|
||||||
|
|
||||||
|
final fights = useMemoized(() {
|
||||||
|
List<List<List<List<int>>>> groupFights = [];
|
||||||
|
final random = Random();
|
||||||
|
for (final group in groups.value) {
|
||||||
|
int rounds = group.length - 1;
|
||||||
|
List<List<List<int>>> groupFight = [];
|
||||||
|
for (int i = 0; i < rounds; i++) {
|
||||||
|
final availableTeams = group.toList();
|
||||||
|
if (group.length % 2 == 1) availableTeams.removeAt(i);
|
||||||
|
final List<List<int>> roundFights = [];
|
||||||
|
while (availableTeams.isNotEmpty) {
|
||||||
|
final team1 = availableTeams.removeAt(0);
|
||||||
|
final team2 = availableTeams.removeAt(i % availableTeams.length);
|
||||||
|
final fight = [team1, team2];
|
||||||
|
fight.shuffle(random);
|
||||||
|
roundFights.add(fight);
|
||||||
|
}
|
||||||
|
groupFight.add(roundFights);
|
||||||
|
}
|
||||||
|
groupFights.add(groupFight);
|
||||||
|
}
|
||||||
|
return groupFights;
|
||||||
|
}, [groups.value]);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Group Bracket Generator'),
|
title: const Text('Group Bracket Generator'),
|
||||||
),
|
actions: [
|
||||||
floatingActionButton: FloatingActionButton(
|
IconButton(
|
||||||
onPressed: !canProceed
|
icon: const Icon(Icons.sync),
|
||||||
? null
|
tooltip: "Auto Generate",
|
||||||
: () {
|
onPressed: () async {
|
||||||
groups.value.add([]);
|
final size = await showDialog(
|
||||||
},
|
context: context,
|
||||||
child: Icon(Icons.navigate_next),
|
builder: (context) {
|
||||||
),
|
return HookBuilder(builder: (context) {
|
||||||
body: Column(
|
final groupsCount = useState(event.teams.length ~/ 2);
|
||||||
mainAxisSize: MainAxisSize.max,
|
return AlertDialog(
|
||||||
children: [
|
title: const Text("Groups"),
|
||||||
Wrap(
|
content: Column(
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
mainAxisSize: MainAxisSize.min,
|
||||||
alignment: WrapAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
for (final team in notInGroupTeams)
|
Text("Groups: ${groupsCount.value}"),
|
||||||
Padding(
|
Slider(
|
||||||
padding: const EdgeInsets.all(8.0),
|
value: groupsCount.value.toDouble(),
|
||||||
child: Draggable<Team>(
|
min: 1,
|
||||||
data: team,
|
max: event.teams.length / 2,
|
||||||
feedback: Material(
|
divisions: (event.teams.length ~/ 2) - 1,
|
||||||
color: Colors.transparent, child: _TeamChip(team)),
|
label: groupsCount.value.toString(),
|
||||||
child: _TeamChip(team),
|
onChanged: (value) {
|
||||||
),
|
groupsCount.value = value.toInt();
|
||||||
),
|
},
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.of(context).pop(0),
|
||||||
|
child: const Text("Cancel"),
|
||||||
|
),
|
||||||
|
FilledButton(
|
||||||
|
onPressed: () =>
|
||||||
|
Navigator.of(context).pop(groupsCount.value),
|
||||||
|
child: const Text("Ok"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if (size == 0) return;
|
||||||
|
final random = Random();
|
||||||
|
final teams = event.teams.toList();
|
||||||
|
teams.shuffle(random);
|
||||||
|
final groupsNew = List.generate(size, (index) => <int>[]);
|
||||||
|
int k = 0;
|
||||||
|
for (int i = 0; i < teams.length; i++) {
|
||||||
|
groupsNew[k].add(teams[i].id);
|
||||||
|
k++;
|
||||||
|
if (k >= size) k = 0;
|
||||||
|
}
|
||||||
|
groups.value = groupsNew;
|
||||||
|
},
|
||||||
),
|
),
|
||||||
Wrap(
|
],
|
||||||
crossAxisAlignment: WrapCrossAlignment.start,
|
leading: BackButton(
|
||||||
alignment: WrapAlignment.start,
|
onPressed: () => context.go('/event/${event.event.id}'),
|
||||||
children: [
|
),
|
||||||
for (final group in groups.value)
|
),
|
||||||
|
floatingActionButton: FloatingActionButton.extended(
|
||||||
|
icon: const Icon(Icons.check),
|
||||||
|
onPressed: canProceed
|
||||||
|
? () async {
|
||||||
|
final repo = await ref.read(eventRepositoryProvider.future);
|
||||||
|
for (final group in fights) {
|
||||||
|
for (final round in group) {
|
||||||
|
for (final fight in round) {
|
||||||
|
final blue = getTeam(fight[0]);
|
||||||
|
final red = getTeam(fight[1]);
|
||||||
|
final start = startTime.value.add(
|
||||||
|
roundTime.value * group.indexOf(round) +
|
||||||
|
startDelay.value *
|
||||||
|
(round.indexOf(fight) +
|
||||||
|
(fights.indexOf(group) * round.length)),
|
||||||
|
);
|
||||||
|
await repo.createFight(
|
||||||
|
event.event.id,
|
||||||
|
start,
|
||||||
|
gamemode.value!,
|
||||||
|
map.value!,
|
||||||
|
blue,
|
||||||
|
red,
|
||||||
|
0,
|
||||||
|
"Gruppe ${fights.indexOf(group) + 1}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context.go("/events/${event.event.id}");
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
label: const Text("Generate")),
|
||||||
|
body: Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: ListView(
|
||||||
|
children: [
|
||||||
|
Wrap(
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
children: [
|
||||||
|
for (final team in notInGroupTeams)
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Draggable<Team>(
|
||||||
|
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<Team>(
|
||||||
|
builder: (context, candidateData, rejectedData) {
|
||||||
|
return ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
minWidth: 200,
|
||||||
|
maxWidth: 200,
|
||||||
|
minHeight: 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(
|
||||||
padding: const EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: DragTarget<Team>(
|
child: DragTarget<Team>(
|
||||||
@ -99,34 +291,8 @@ class GroupBracketGenerator extends HookConsumerWidget {
|
|||||||
height: 200,
|
height: 200,
|
||||||
child: Card(
|
child: Card(
|
||||||
child: Column(
|
child: Column(
|
||||||
children: [
|
children: const [
|
||||||
Text('Group ${groups.value.indexOf(group) + 1}'),
|
Text('New Group'),
|
||||||
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
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -135,47 +301,114 @@ class GroupBracketGenerator extends HookConsumerWidget {
|
|||||||
onWillAccept: (data) => true,
|
onWillAccept: (data) => true,
|
||||||
onAccept: (data) {
|
onAccept: (data) {
|
||||||
groups.value = [
|
groups.value = [
|
||||||
for (final addGroup in groups.value)
|
...groups.value,
|
||||||
if (addGroup.contains(data.id))
|
[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<Team>(
|
Divider(),
|
||||||
builder: (context, candidateData, rejectedData) {
|
Column(
|
||||||
return SizedBox(
|
children: [
|
||||||
width: 200,
|
Row(
|
||||||
height: 200,
|
children: [
|
||||||
child: Card(
|
const Text('Start Time:'),
|
||||||
child: Column(
|
DateTimeEditor((p0) {
|
||||||
children: const [
|
startTime.value = p0;
|
||||||
Text('New Group'),
|
}, startTime.value, true),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text('Round Time: ${roundTime.value.inMinutes} min'),
|
||||||
|
Slider(
|
||||||
|
value: roundTime.value.inMinutes.toDouble(),
|
||||||
|
onChanged: (t) =>
|
||||||
|
roundTime.value = Duration(minutes: t.round()),
|
||||||
|
min: 5,
|
||||||
|
max: 60,
|
||||||
|
divisions: 60 - 5,
|
||||||
|
label: '${roundTime.value.inMinutes} min'),
|
||||||
|
Text('Start Delay: ${startDelay.value.inSeconds} sec'),
|
||||||
|
Slider(
|
||||||
|
value: startDelay.value.inSeconds.toDouble(),
|
||||||
|
onChanged: (t) =>
|
||||||
|
startDelay.value = Duration(seconds: t.round()),
|
||||||
|
min: 0,
|
||||||
|
max: 30,
|
||||||
|
divisions: 30,
|
||||||
|
label: '${startDelay.value.inSeconds} sec'),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
const Text('Gamemode:'),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final newmode = await showSearch(
|
||||||
|
context: context,
|
||||||
|
delegate: GamemodeSearchDelegate(
|
||||||
|
await ref.read(fightServersProvider.future)));
|
||||||
|
gamemode.value = newmode;
|
||||||
|
},
|
||||||
|
child: Text(gamemode.value ?? 'None')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
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')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Divider(),
|
||||||
|
for (final group in fights)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text('Group ${fights.indexOf(group) + 1}',
|
||||||
|
style: Theme.of(context).textTheme.headlineMedium),
|
||||||
|
for (final round in group)
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Text('Round ${group.indexOf(round) + 1}',
|
||||||
|
style:
|
||||||
|
Theme.of(context).textTheme.headlineSmall),
|
||||||
|
const Divider(),
|
||||||
|
for (final fight in round)
|
||||||
|
ListTile(
|
||||||
|
leading: Chip(
|
||||||
|
label: Text(
|
||||||
|
kDateFormat.format(
|
||||||
|
startTime.value.add(
|
||||||
|
roundTime.value * group.indexOf(round) +
|
||||||
|
startDelay.value *
|
||||||
|
(round.indexOf(fight) +
|
||||||
|
(fights.indexOf(group) *
|
||||||
|
round.length)),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
'${getTeam(fight[0]).name} vs ${getTeam(fight[1]).name}'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
);
|
)
|
||||||
},
|
],
|
||||||
onWillAccept: (data) => true,
|
),
|
||||||
onAccept: (data) {
|
],
|
||||||
groups.value = [
|
),
|
||||||
...groups.value,
|
|
||||||
[data.id],
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -24,7 +24,6 @@ import 'package:flutter_hooks/flutter_hooks.dart';
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
import 'package:steamwar_multitool/src/provider/mods.dart';
|
|
||||||
import 'package:steamwar_multitool/src/provider/provider.dart';
|
import 'package:steamwar_multitool/src/provider/provider.dart';
|
||||||
import 'package:steamwar_multitool/src/screens/home/lists/events_list.dart';
|
import 'package:steamwar_multitool/src/screens/home/lists/events_list.dart';
|
||||||
|
|
||||||
|
@ -19,4 +19,4 @@
|
|||||||
|
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
final kDateFormat = DateFormat("dd.MM.yyyy HH:mm");
|
final kDateFormat = DateFormat("dd.MM.yyyy HH:mm:ss");
|
||||||
|
26
lib/src/util/functions.dart
Normale Datei
26
lib/src/util/functions.dart
Normale Datei
@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
* 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/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int wrapAround(int value, int max) {
|
||||||
|
if (value < max) {
|
||||||
|
return value;
|
||||||
|
} else {
|
||||||
|
return value % max;
|
||||||
|
}
|
||||||
|
}
|
In neuem Issue referenzieren
Einen Benutzer sperren