Fehlende Methoden für Games Datenbank inplementieren #76
@@ -8,6 +8,19 @@ class CustomTheme {
|
||||
static Color onBoxColor = const Color(0xFF181818);
|
||||
static Color boxBorder = const Color(0xFF272727);
|
||||
|
||||
static BoxDecoration standardBoxDecoration = BoxDecoration(
|
||||
color: boxColor,
|
||||
border: Border.all(color: boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
);
|
||||
|
||||
static BoxDecoration highlightedBoxDecoration = BoxDecoration(
|
||||
color: boxColor,
|
||||
border: Border.all(color: primaryColor),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
boxShadow: [BoxShadow(color: primaryColor.withAlpha(120), blurRadius: 12)],
|
||||
);
|
||||
|
||||
static AppBarTheme appBarTheme = AppBarTheme(
|
||||
backgroundColor: backgroundColor,
|
||||
foregroundColor: Colors.white,
|
||||
|
||||
@@ -22,3 +22,10 @@ enum ImportResult {
|
||||
/// - [ExportResult.canceled]: The export operation was canceled by the user.
|
||||
/// - [ExportResult.unknownException]: An exception occurred during export.
|
||||
enum ExportResult { success, canceled, unknownException }
|
||||
|
||||
/// Different rulesets available for games
|
||||
/// - [Ruleset.singleWinner]: The game is won by a single player
|
||||
/// - [Ruleset.singleLoser]: The game is lost by a single player
|
||||
/// - [Ruleset.mostPoints]: The player with the most points wins.
|
||||
/// - [Ruleset.lastPoints]: The player with the fewest points wins.
|
||||
enum Ruleset { singleWinner, singleLoser, mostPoints, lastPoints }
|
||||
|
||||
@@ -78,7 +78,7 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
|
||||
);
|
||||
|
||||
if (game.players != null) {
|
||||
await db.playerDao.addPlayers(players: game.players!);
|
||||
await db.playerDao.addPlayersAsList(players: game.players!);
|
||||
for (final p in game.players ?? []) {
|
||||
await db.playerGameDao.addPlayerToGame(
|
||||
gameId: game.id,
|
||||
@@ -89,12 +89,18 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
|
||||
|
||||
if (game.group != null) {
|
||||
await db.groupDao.addGroup(group: game.group!);
|
||||
await db.groupGameDao.addGroupToGame(game.id, game.group!.id);
|
||||
await db.groupGameDao.addGroupToGame(
|
||||
gameId: game.id,
|
||||
groupId: game.group!.id,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Future<void> addGames({required List<Game> games}) async {
|
||||
/// Adds multiple [Game]s to the database in a batch operation.
|
||||
/// Also adds associated players and groups if they exist.
|
||||
/// If the [games] list is empty, the method returns immediately.
|
||||
Future<void> addGamesAsList({required List<Game> games}) async {
|
||||
if (games.isEmpty) return;
|
||||
await db.transaction(() async {
|
||||
// Add all games in batch
|
||||
@@ -253,4 +259,62 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
|
||||
final rowsAffected = await query.go();
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
|
||||
/// Sets the winner of the game with the given [gameId] to the player with
|
||||
/// the given [winnerId].
|
||||
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
|
||||
Future<bool> setWinner({
|
||||
required String gameId,
|
||||
required String winnerId,
|
||||
}) async {
|
||||
final query = update(gameTable)..where((g) => g.id.equals(gameId));
|
||||
final rowsAffected = await query.write(
|
||||
GameTableCompanion(winnerId: Value(winnerId)),
|
||||
);
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
|
||||
/// Retrieves the winner of the game with the given [gameId].
|
||||
/// Returns the [Player] who won the game, or `null` if no winner is set.
|
||||
Future<Player?> getWinner({required String gameId}) async {
|
||||
final query = select(gameTable)..where((g) => g.id.equals(gameId));
|
||||
final result = await query.getSingleOrNull();
|
||||
if (result == null || result.winnerId == null) {
|
||||
return null;
|
||||
}
|
||||
final winner = await db.playerDao.getPlayerById(playerId: result.winnerId!);
|
||||
return winner;
|
||||
}
|
||||
|
||||
/// Removes the winner of the game with the given [gameId].
|
||||
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
|
||||
Future<bool> removeWinner({required String gameId}) async {
|
||||
final query = update(gameTable)..where((g) => g.id.equals(gameId));
|
||||
final rowsAffected = await query.write(
|
||||
const GameTableCompanion(winnerId: Value(null)),
|
||||
);
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
|
||||
/// Checks if the game with the given [gameId] has a winner set.
|
||||
/// Returns `true` if a winner is set, otherwise `false`.
|
||||
Future<bool> hasWinner({required String gameId}) async {
|
||||
final query = select(gameTable)
|
||||
..where((g) => g.id.equals(gameId) & g.winnerId.isNotNull());
|
||||
final result = await query.getSingleOrNull();
|
||||
return result != null;
|
||||
}
|
||||
|
||||
/// Changes the title of the game with the given [gameId] to [newName].
|
||||
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
|
||||
Future<bool> updateGameName({
|
||||
required String gameId,
|
||||
required String newName,
|
||||
}) async {
|
||||
final query = update(gameTable)..where((g) => g.id.equals(gameId));
|
||||
final rowsAffected = await query.write(
|
||||
GameTableCompanion(name: Value(newName)),
|
||||
);
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
|
||||
|
||||
/// Adds multiple groups to the database.
|
||||
/// Also adds the group's members to the [PlayerGroupTable].
|
||||
Future<void> addGroups({required List<Group> groups}) async {
|
||||
Future<void> addGroupsAsList({required List<Group> groups}) async {
|
||||
if (groups.isEmpty) return;
|
||||
await db.transaction(() async {
|
||||
// Deduplicate groups by id - keep first occurrence
|
||||
|
||||
@@ -12,7 +12,13 @@ class GroupGameDao extends DatabaseAccessor<AppDatabase>
|
||||
|
||||
/// Associates a group with a game by inserting a record into the
|
||||
/// [GroupGameTable].
|
||||
Future<void> addGroupToGame(String gameId, String groupId) async {
|
||||
Future<void> addGroupToGame({
|
||||
required String gameId,
|
||||
required String groupId,
|
||||
|
flixcoo marked this conversation as resolved
|
||||
}) async {
|
||||
if (await gameHasGroup(gameId: gameId)) {
|
||||
throw Exception('Game already has a group');
|
||||
}
|
||||
await into(groupGameTable).insert(
|
||||
GroupGameTableCompanion.insert(groupId: groupId, gameId: gameId),
|
||||
mode: InsertMode.insertOrReplace,
|
||||
@@ -76,4 +82,17 @@ class GroupGameDao extends DatabaseAccessor<AppDatabase>
|
||||
final rowsAffected = await query.go();
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
|
||||
/// Updates the group associated with a game to [newGroupId] based on
|
||||
/// [gameId].
|
||||
/// Returns `true` if more than 0 rows were affected, otherwise `false`.
|
||||
Future<bool> updateGroupOfGame({
|
||||
required String gameId,
|
||||
required String newGroupId,
|
||||
}) async {
|
||||
final updatedRows =
|
||||
await (update(groupGameTable)..where((g) => g.gameId.equals(gameId)))
|
||||
.write(GroupGameTableCompanion(groupId: Value(newGroupId)));
|
||||
return updatedRows > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
|
||||
}
|
||||
|
||||
/// Adds multiple [players] to the database in a batch operation.
|
||||
Future<bool> addPlayers({required List<Player> players}) async {
|
||||
Future<bool> addPlayersAsList({required List<Player> players}) async {
|
||||
if (players.isEmpty) return false;
|
||||
|
||||
await db.batch(
|
||||
|
||||
@@ -79,4 +79,50 @@ class PlayerGameDao extends DatabaseAccessor<AppDatabase>
|
||||
final rowsAffected = await query.go();
|
||||
return rowsAffected > 0;
|
||||
}
|
||||
|
||||
/// Updates the players associated with a game based on the provided
|
||||
/// [newPlayer] list. It adds new players and removes players that are no
|
||||
/// longer associated with the game.
|
||||
Future<void> updatePlayersFromGame({
|
||||
required String gameId,
|
||||
required List<Player> newPlayer,
|
||||
}) async {
|
||||
final currentPlayers = await getPlayersOfGame(gameId: gameId);
|
||||
// Create sets of player IDs for easy comparison
|
||||
final currentPlayerIds = currentPlayers?.map((p) => p.id).toSet() ?? {};
|
||||
final newPlayerIdsSet = newPlayer.map((p) => p.id).toSet();
|
||||
|
||||
// Determine players to add and remove
|
||||
final playersToAdd = newPlayerIdsSet.difference(currentPlayerIds);
|
||||
final playersToRemove = currentPlayerIds.difference(newPlayerIdsSet);
|
||||
|
||||
db.transaction(() async {
|
||||
// Remove old players
|
||||
if (playersToRemove.isNotEmpty) {
|
||||
await (delete(playerGameTable)..where(
|
||||
(pg) =>
|
||||
pg.gameId.equals(gameId) &
|
||||
pg.playerId.isIn(playersToRemove.toList()),
|
||||
))
|
||||
.go();
|
||||
}
|
||||
|
||||
// Add new players
|
||||
if (playersToAdd.isNotEmpty) {
|
||||
final inserts = playersToAdd
|
||||
.map(
|
||||
(id) =>
|
||||
PlayerGameTableCompanion.insert(playerId: id, gameId: gameId),
|
||||
)
|
||||
.toList();
|
||||
await Future.wait(
|
||||
inserts.map(
|
||||
(c) => into(
|
||||
playerGameTable,
|
||||
).insert(c, mode: InsertMode.insertOrReplace),
|
||||
),
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/group_tile.dart';
|
||||
|
||||
class ChooseGroupView extends StatefulWidget {
|
||||
final List<Group> groups;
|
||||
final int initialGroupIndex;
|
||||
|
||||
const ChooseGroupView({
|
||||
super.key,
|
||||
required this.groups,
|
||||
required this.initialGroupIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChooseGroupView> createState() => _ChooseGroupViewState();
|
||||
}
|
||||
|
||||
class _ChooseGroupViewState extends State<ChooseGroupView> {
|
||||
late int selectedGroupIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
selectedGroupIndex = widget.initialGroupIndex;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
title: const Text(
|
||||
'Choose Group',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: widget.groups.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
setState(() {
|
||||
selectedGroupIndex = index;
|
||||
});
|
||||
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(context).pop(widget.groups[index]);
|
||||
});
|
||||
},
|
||||
child: GroupTile(
|
||||
group: widget.groups[index],
|
||||
isHighlighted: selectedGroupIndex == index,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/ruleset_list_tile.dart';
|
||||
|
||||
class ChooseRulesetView extends StatefulWidget {
|
||||
final List<(Ruleset, String, String)> rulesets;
|
||||
final int initialRulesetIndex;
|
||||
const ChooseRulesetView({
|
||||
super.key,
|
||||
required this.rulesets,
|
||||
required this.initialRulesetIndex,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChooseRulesetView> createState() => _ChooseRulesetViewState();
|
||||
}
|
||||
|
||||
class _ChooseRulesetViewState extends State<ChooseRulesetView> {
|
||||
late int selectedRulesetIndex;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
selectedRulesetIndex = widget.initialRulesetIndex;
|
||||
super.initState();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return DefaultTabController(
|
||||
length: 2,
|
||||
initialIndex: 0,
|
||||
child: Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
title: const Text(
|
||||
'Choose Ruleset',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
Container(
|
||||
color: CustomTheme.backgroundColor,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
|
||||
child: TabBar(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 5),
|
||||
// Label Settings
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
labelColor: Colors.white,
|
||||
unselectedLabelStyle: const TextStyle(fontSize: 14),
|
||||
unselectedLabelColor: Colors.white70,
|
||||
// Indicator Settings
|
||||
indicator: CustomTheme.standardBoxDecoration,
|
||||
indicatorSize: TabBarIndicatorSize.tab,
|
||||
indicatorWeight: 1,
|
||||
indicatorPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20,
|
||||
vertical: 0,
|
||||
),
|
||||
// Divider Settings
|
||||
dividerHeight: 0,
|
||||
tabs: const [
|
||||
Tab(text: 'Rulesets'),
|
||||
Tab(text: 'Gametypes'),
|
||||
],
|
||||
),
|
||||
),
|
||||
const Divider(
|
||||
indent: 30,
|
||||
endIndent: 30,
|
||||
thickness: 3,
|
||||
radius: BorderRadius.all(Radius.circular(12)),
|
||||
),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
children: [
|
||||
ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: widget.rulesets.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return RulesetListTile(
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
selectedRulesetIndex = index;
|
||||
});
|
||||
Future.delayed(const Duration(milliseconds: 500), () {
|
||||
if (!context.mounted) return;
|
||||
Navigator.of(
|
||||
context,
|
||||
).pop(widget.rulesets[index].$1);
|
||||
});
|
||||
},
|
||||
title: widget.rulesets[index].$2,
|
||||
description: widget.rulesets[index].$3,
|
||||
isHighlighted: selectedRulesetIndex == index,
|
||||
);
|
||||
},
|
||||
),
|
||||
const Center(
|
||||
child: Text(
|
||||
'No gametypes available',
|
||||
style: TextStyle(color: Colors.white70),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/core/enums.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/game.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/create_game/choose_group_view.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/create_game/choose_ruleset_view.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/player_selection.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/choose_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CreateGameView extends StatefulWidget {
|
||||
const CreateGameView({super.key});
|
||||
|
||||
@override
|
||||
State<CreateGameView> createState() => _CreateGameViewState();
|
||||
}
|
||||
|
||||
class _CreateGameViewState extends State<CreateGameView> {
|
||||
/// Reference to the app database
|
||||
|
sneeex marked this conversation as resolved
gelbeinhalb
commented
Warum so viele useless Kommentare? ChatGPT? Warum so viele useless Kommentare? ChatGPT?
sneeex
commented
Ja war wo anders auch so, finde das auch zu viel, er meinte beim anderen mal aber zum verständnis und so Ja war wo anders auch so, finde das auch zu viel, er meinte beim anderen mal aber zum verständnis und so
gelbeinhalb
commented
also ich check das ja an sich, wenn das denn nur halt überall so wäre finde es gibt auf jeden fall parts in dem code die schwerer zu verstehen sind als das und garkeine kommentare haben also ich check das ja an sich, wenn das denn nur halt überall so wäre
finde es gibt auf jeden fall parts in dem code die schwerer zu verstehen sind als das und garkeine kommentare haben
flixcoo
commented
Finde das irgendwie geiler wenn jede variable da oben ne beschreibung hat, wenn man über sie hovert auch (mit den Finde das irgendwie geiler wenn jede variable da oben ne beschreibung hat, wenn man über sie hovert auch (mit den `///`), also finde das sinnvoller
gelbeinhalb
commented
Warum dann aber nicht überall? oder kommt das im refactor? Warum dann aber nicht überall? oder kommt das im refactor?
flixcoo
commented
Ja würde ich gerne machen, auch als vorlage für alle kommenden Variablen, Methoden & Klassen Ja würde ich gerne machen, auch als vorlage für alle kommenden Variablen, Methoden & Klassen
|
||||
late final AppDatabase db;
|
||||
|
||||
/// Futures to load all groups and players from the database
|
||||
late Future<List<Group>> _allGroupsFuture;
|
||||
|
||||
/// Future to load all players from the database
|
||||
late Future<List<Player>> _allPlayersFuture;
|
||||
|
||||
/// Controller for the game name input field
|
||||
final TextEditingController _gameNameController = TextEditingController();
|
||||
|
||||
/// List of all groups from the database
|
||||
List<Group> groupsList = [];
|
||||
|
||||
/// List of all players from the database
|
||||
List<Player> playerList = [];
|
||||
|
||||
/// The currently selected group
|
||||
Group? selectedGroup;
|
||||
|
||||
/// The index of the currently selected group in [groupsList] to mark it in
|
||||
/// the [ChooseGroupView]
|
||||
int selectedGroupIndex = -1;
|
||||
|
||||
/// The currently selected ruleset
|
||||
Ruleset? selectedRuleset;
|
||||
|
||||
/// The index of the currently selected ruleset in [rulesets] to mark it in
|
||||
/// the [ChooseRulesetView]
|
||||
int selectedRulesetIndex = -1;
|
||||
|
||||
/// The currently selected players
|
||||
List<Player>? selectedPlayers;
|
||||
|
||||
/// List of available rulesets with their display names and descriptions
|
||||
/// as tuples of (Ruleset, String, String)
|
||||
List<(Ruleset, String, String)> rulesets = [
|
||||
(
|
||||
Ruleset.singleWinner,
|
||||
'Single Winner',
|
||||
'Exactly one winner is chosen; ties are resolved by a predefined tiebreaker.',
|
||||
),
|
||||
(
|
||||
Ruleset.singleLoser,
|
||||
'Single Loser',
|
||||
'Exactly one loser is determined; last place receives the penalty or consequence.',
|
||||
),
|
||||
(
|
||||
Ruleset.mostPoints,
|
||||
'Most Points',
|
||||
'Traditional ruleset: the player with the most points wins.',
|
||||
),
|
||||
(
|
||||
Ruleset.lastPoints,
|
||||
'Least Points',
|
||||
'Inverse scoring: the player with the fewest points wins.',
|
||||
),
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
db = Provider.of<AppDatabase>(context, listen: false);
|
||||
|
||||
_allGroupsFuture = db.groupDao.getAllGroups();
|
||||
_allPlayersFuture = db.playerDao.getAllPlayers();
|
||||
|
||||
Future.wait([_allGroupsFuture, _allPlayersFuture]).then((result) async {
|
||||
groupsList = result[0] as List<Group>;
|
||||
playerList = result[1] as List<Player>;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
appBar: AppBar(
|
||||
backgroundColor: CustomTheme.backgroundColor,
|
||||
scrolledUnderElevation: 0,
|
||||
title: const Text(
|
||||
'Create new game',
|
||||
style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
|
||||
),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
child: TextInputField(
|
||||
controller: _gameNameController,
|
||||
hintText: 'Game name',
|
||||
onChanged: (value) {
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
ChooseTile(
|
||||
title: 'Ruleset',
|
||||
trailingText: selectedRuleset == null
|
||||
? 'None'
|
||||
: translateRulesetToString(selectedRuleset!),
|
||||
onPressed: () async {
|
||||
selectedRuleset = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChooseRulesetView(
|
||||
rulesets: rulesets,
|
||||
initialRulesetIndex: selectedRulesetIndex,
|
||||
),
|
||||
),
|
||||
);
|
||||
selectedRulesetIndex = rulesets.indexWhere(
|
||||
(r) => r.$1 == selectedRuleset,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
ChooseTile(
|
||||
title: 'Group',
|
||||
trailingText: selectedGroup == null
|
||||
? 'None'
|
||||
: selectedGroup!.name,
|
||||
onPressed: () async {
|
||||
selectedGroup = await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChooseGroupView(
|
||||
groups: groupsList,
|
||||
initialGroupIndex: selectedGroupIndex,
|
||||
),
|
||||
),
|
||||
);
|
||||
selectedGroupIndex = groupsList.indexWhere(
|
||||
(g) => g.id == selectedGroup?.id,
|
||||
);
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: PlayerSelection(
|
||||
key: ValueKey(selectedGroup?.id ?? 'no_group'),
|
||||
initialPlayers: selectedGroup == null
|
||||
? playerList
|
||||
: playerList
|
||||
.where(
|
||||
(p) => !selectedGroup!.members.any(
|
||||
(m) => m.id == p.id,
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
selectedPlayers = value;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
CustomWidthButton(
|
||||
text: 'Create game',
|
||||
sizeRelativeToWidth: 0.95,
|
||||
buttonType: ButtonType.primary,
|
||||
onPressed: _enableCreateGameButton()
|
||||
? () async {
|
||||
Game game = Game(
|
||||
name: _gameNameController.text.trim(),
|
||||
createdAt: DateTime.now(),
|
||||
group: selectedGroup!,
|
||||
players: selectedPlayers,
|
||||
);
|
||||
// TODO: Replace with navigation to GameResultView()
|
||||
print('Created game: $game');
|
||||
Navigator.pop(context);
|
||||
}
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Translates a [Ruleset] enum value to its corresponding string representation.
|
||||
String translateRulesetToString(Ruleset ruleset) {
|
||||
switch (ruleset) {
|
||||
case Ruleset.singleWinner:
|
||||
return 'Single Winner';
|
||||
case Ruleset.singleLoser:
|
||||
return 'Single Loser';
|
||||
case Ruleset.mostPoints:
|
||||
return 'Most Points';
|
||||
case Ruleset.lastPoints:
|
||||
return 'Least Points';
|
||||
}
|
||||
}
|
||||
|
||||
/// Determines whether the "Create Game" button should be enabled based on
|
||||
/// the current state of the input fields.
|
||||
bool _enableCreateGameButton() {
|
||||
return _gameNameController.text.isNotEmpty &&
|
||||
(selectedGroup != null ||
|
||||
(selectedPlayers != null && selectedPlayers!.isNotEmpty)) &&
|
||||
selectedRuleset != null;
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/player_selection.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input_field.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/text_input_field.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class CreateGroupView extends StatefulWidget {
|
||||
|
||||
@@ -5,10 +5,10 @@ import 'package:game_tracker/data/dto/game.dart';
|
||||
import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/views/main_menu/create_group_view.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/game_history_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/buttons/custom_width_button.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/game_history_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class GameHistoryView extends StatefulWidget {
|
||||
@@ -38,9 +38,7 @@ class _GameHistoryViewState extends State<GameHistoryView> {
|
||||
],
|
||||
),
|
||||
winner: Player(name: 'Skeleton Player 1'),
|
||||
players: [
|
||||
Player(name: 'Skeleton Player 6')
|
||||
],
|
||||
players: [Player(name: 'Skeleton Player 6')],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -67,49 +65,53 @@ class _GameHistoryViewState extends State<GameHistoryView> {
|
||||
children: [
|
||||
FutureBuilder<List<Game>>(
|
||||
future: _gameListFuture,
|
||||
builder: (BuildContext context, AsyncSnapshot<List<Game>> snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.report,
|
||||
title: 'Error',
|
||||
message: 'Game data could not be loaded',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
(!snapshot.hasData || snapshot.data!.isEmpty)) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.report,
|
||||
title: 'Error',
|
||||
message: 'No Games Available',
|
||||
),
|
||||
);
|
||||
}
|
||||
builder:
|
||||
(BuildContext context, AsyncSnapshot<List<Game>> snapshot) {
|
||||
if (snapshot.hasError) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.report,
|
||||
title: 'Error',
|
||||
message: 'Game data could not be loaded',
|
||||
),
|
||||
);
|
||||
}
|
||||
if (snapshot.connectionState == ConnectionState.done &&
|
||||
(!snapshot.hasData || snapshot.data!.isEmpty)) {
|
||||
return const Center(
|
||||
child: TopCenteredMessage(
|
||||
icon: Icons.report,
|
||||
title: 'Error',
|
||||
message: 'No Games Available',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final List<Game> games = (isLoading
|
||||
? skeletonData
|
||||
: (snapshot.data ?? [])
|
||||
..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
|
||||
.toList();
|
||||
final List<Game> games =
|
||||
(isLoading ? skeletonData : (snapshot.data ?? [])
|
||||
..sort(
|
||||
(a, b) => b.createdAt.compareTo(a.createdAt),
|
||||
))
|
||||
.toList();
|
||||
|
||||
return AppSkeleton(
|
||||
enabled: isLoading,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: games.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == games.length) {
|
||||
return SizedBox(
|
||||
height: MediaQuery.paddingOf(context).bottom - 80,
|
||||
);
|
||||
}
|
||||
return GameHistoryTile(game: games[index]); // Placeholder
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
return AppSkeleton(
|
||||
enabled: isLoading,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.only(bottom: 85),
|
||||
itemCount: games.length + 1,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
if (index == games.length) {
|
||||
return SizedBox(
|
||||
height: MediaQuery.paddingOf(context).bottom - 80,
|
||||
);
|
||||
}
|
||||
return GameHistoryTile(
|
||||
game: games[index],
|
||||
); // Placeholder
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
@@ -135,4 +137,4 @@ class _GameHistoryViewState extends State<GameHistoryView> {
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,6 @@ class _GroupsViewState extends State<GroupsView> {
|
||||
);
|
||||
},
|
||||
),
|
||||
|
||||
Positioned(
|
||||
bottom: MediaQuery.paddingOf(context).bottom,
|
||||
child: CustomWidthButton(
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:game_tracker/core/custom_theme.dart';
|
||||
import 'package:game_tracker/data/db/database.dart';
|
||||
import 'package:game_tracker/data/dto/player.dart';
|
||||
import 'package:game_tracker/presentation/widgets/app_skeleton.dart';
|
||||
import 'package:game_tracker/presentation/widgets/custom_search_bar.dart';
|
||||
import 'package:game_tracker/presentation/widgets/text_input/custom_search_bar.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_list_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
import 'package:game_tracker/presentation/widgets/top_centered_message.dart';
|
||||
@@ -11,8 +11,13 @@ import 'package:provider/provider.dart';
|
||||
|
||||
class PlayerSelection extends StatefulWidget {
|
||||
final Function(List<Player> value) onChanged;
|
||||
final List<Player> initialPlayers;
|
||||
|
||||
const PlayerSelection({super.key, required this.onChanged});
|
||||
const PlayerSelection({
|
||||
super.key,
|
||||
required this.onChanged,
|
||||
this.initialPlayers = const [],
|
||||
});
|
||||
|
||||
@override
|
||||
State<PlayerSelection> createState() => _PlayerSelectionState();
|
||||
@@ -46,9 +51,14 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
suggestedPlayers = skeletonData;
|
||||
_allPlayersFuture.then((loadedPlayers) {
|
||||
setState(() {
|
||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||
allPlayers = [...loadedPlayers];
|
||||
suggestedPlayers = [...loadedPlayers];
|
||||
if (widget.initialPlayers.isNotEmpty) {
|
||||
allPlayers = [...widget.initialPlayers];
|
||||
suggestedPlayers = [...widget.initialPlayers];
|
||||
} else {
|
||||
loadedPlayers.sort((a, b) => a.name.compareTo(b.name));
|
||||
allPlayers = [...loadedPlayers];
|
||||
suggestedPlayers = [...loadedPlayers];
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -58,11 +68,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
43
lib/presentation/widgets/tiles/choose_tile.dart
Normal file
43
lib/presentation/widgets/tiles/choose_tile.dart
Normal file
@@ -0,0 +1,43 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
class ChooseTile extends StatefulWidget {
|
||||
final String title;
|
||||
final VoidCallback? onPressed;
|
||||
final String? trailingText;
|
||||
const ChooseTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
this.trailingText,
|
||||
this.onPressed,
|
||||
});
|
||||
|
||||
@override
|
||||
State<ChooseTile> createState() => _ChooseTileState();
|
||||
}
|
||||
|
||||
class _ChooseTileState extends State<ChooseTile> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: widget.onPressed,
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 15),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
children: [
|
||||
Text(
|
||||
widget.title,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Spacer(),
|
||||
if (widget.trailingText != null) Text(widget.trailingText!),
|
||||
const SizedBox(width: 10),
|
||||
const Icon(Icons.arrow_forward_ios, size: 16),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -4,20 +4,20 @@ import 'package:game_tracker/data/dto/group.dart';
|
||||
import 'package:game_tracker/presentation/widgets/tiles/text_icon_tile.dart';
|
||||
|
||||
class GroupTile extends StatelessWidget {
|
||||
const GroupTile({super.key, required this.group});
|
||||
const GroupTile({super.key, required this.group, this.isHighlighted = false});
|
||||
|
||||
final Group group;
|
||||
final bool isHighlighted;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
return AnimatedContainer(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 5, horizontal: 10),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: isHighlighted
|
||||
? CustomTheme.highlightedBoxDecoration
|
||||
: CustomTheme.standardBoxDecoration,
|
||||
duration: const Duration(milliseconds: 150),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
@@ -29,11 +29,7 @@ class _InfoTileState extends State<InfoTile> {
|
||||
padding: widget.padding ?? const EdgeInsets.all(12),
|
||||
height: widget.height,
|
||||
width: widget.width ?? 380,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
|
||||
@@ -29,11 +29,7 @@ class _QuickInfoTileState extends State<QuickInfoTile> {
|
||||
padding: widget.padding ?? const EdgeInsets.all(12),
|
||||
height: widget.height ?? 110,
|
||||
width: widget.width ?? 180,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
|
||||
55
lib/presentation/widgets/tiles/ruleset_list_tile.dart
Normal file
55
lib/presentation/widgets/tiles/ruleset_list_tile.dart
Normal file
@@ -0,0 +1,55 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:game_tracker/core/custom_theme.dart';
|
||||
|
||||
class RulesetListTile extends StatelessWidget {
|
||||
final String title;
|
||||
final String description;
|
||||
final VoidCallback? onPressed;
|
||||
final bool isHighlighted;
|
||||
|
||||
const RulesetListTile({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.description,
|
||||
this.onPressed,
|
||||
this.isHighlighted = false,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GestureDetector(
|
||||
onTap: onPressed,
|
||||
child: AnimatedContainer(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 10),
|
||||
padding: const EdgeInsets.symmetric(vertical: 6, horizontal: 12),
|
||||
decoration: isHighlighted
|
||||
? CustomTheme.highlightedBoxDecoration
|
||||
: CustomTheme.standardBoxDecoration,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
title,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 5),
|
||||
Text(description, style: const TextStyle(fontSize: 14)),
|
||||
const SizedBox(height: 2.5),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -26,11 +26,7 @@ class SettingsListTile extends StatelessWidget {
|
||||
child: Container(
|
||||
margin: EdgeInsets.zero,
|
||||
padding: const EdgeInsets.symmetric(vertical: 10, horizontal: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
|
||||
@@ -18,11 +18,7 @@ class TextIconListTile extends StatelessWidget {
|
||||
return Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 15),
|
||||
decoration: BoxDecoration(
|
||||
color: CustomTheme.boxColor,
|
||||
border: Border.all(color: CustomTheme.boxBorder),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
decoration: CustomTheme.standardBoxDecoration,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
|
||||
@@ -110,9 +110,9 @@ class DataTransferService {
|
||||
.toList() ??
|
||||
[];
|
||||
|
||||
await db.playerDao.addPlayers(players: importedPlayers);
|
||||
await db.groupDao.addGroups(groups: importedGroups);
|
||||
await db.gameDao.addGames(games: importedGames);
|
||||
await db.playerDao.addPlayersAsList(players: importedPlayers);
|
||||
await db.groupDao.addGroupsAsList(groups: importedGroups);
|
||||
await db.gameDao.addGamesAsList(games: importedGames);
|
||||
} else {
|
||||
return ImportResult.invalidSchema;
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('Adding and fetching multiple games works correctly', () async {
|
||||
await database.gameDao.addGames(
|
||||
await database.gameDao.addGamesAsList(
|
||||
games: [testGame1, testGame2, testGameOnlyGroup, testGameOnlyPlayers],
|
||||
);
|
||||
|
||||
@@ -234,5 +234,97 @@ void main() {
|
||||
gameCount = await database.gameDao.getGameCount();
|
||||
expect(gameCount, 0);
|
||||
});
|
||||
|
||||
test('Checking if game has winner works correclty', () async {
|
||||
await database.gameDao.addGame(game: testGame1);
|
||||
await database.gameDao.addGame(game: testGameOnlyGroup);
|
||||
|
||||
var hasWinner = await database.gameDao.hasWinner(gameId: testGame1.id);
|
||||
expect(hasWinner, true);
|
||||
|
||||
hasWinner = await database.gameDao.hasWinner(
|
||||
gameId: testGameOnlyGroup.id,
|
||||
);
|
||||
expect(hasWinner, false);
|
||||
});
|
||||
|
||||
test('Fetching the winner of a game works correctly', () async {
|
||||
await database.gameDao.addGame(game: testGame1);
|
||||
|
||||
final winner = await database.gameDao.getWinner(gameId: testGame1.id);
|
||||
if (winner == null) {
|
||||
fail('Winner is null');
|
||||
} else {
|
||||
expect(winner.id, testGame1.winner!.id);
|
||||
expect(winner.name, testGame1.winner!.name);
|
||||
expect(winner.createdAt, testGame1.winner!.createdAt);
|
||||
}
|
||||
});
|
||||
|
||||
test('Updating the winner of a game works correctly', () async {
|
||||
await database.gameDao.addGame(game: testGame1);
|
||||
|
||||
final winner = await database.gameDao.getWinner(gameId: testGame1.id);
|
||||
if (winner == null) {
|
||||
fail('Winner is null');
|
||||
} else {
|
||||
expect(winner.id, testGame1.winner!.id);
|
||||
expect(winner.name, testGame1.winner!.name);
|
||||
expect(winner.createdAt, testGame1.winner!.createdAt);
|
||||
expect(winner.id, testPlayer4.id);
|
||||
expect(winner.id != testPlayer5.id, true);
|
||||
}
|
||||
|
||||
await database.gameDao.setWinner(
|
||||
gameId: testGame1.id,
|
||||
winnerId: testPlayer5.id,
|
||||
);
|
||||
|
||||
final newWinner = await database.gameDao.getWinner(gameId: testGame1.id);
|
||||
|
||||
if (newWinner == null) {
|
||||
fail('New winner is null');
|
||||
} else {
|
||||
expect(newWinner.id, testPlayer5.id);
|
||||
expect(newWinner.name, testPlayer5.name);
|
||||
expect(newWinner.createdAt, testPlayer5.createdAt);
|
||||
}
|
||||
});
|
||||
|
||||
test('Removing a winner works correctly', () async {
|
||||
await database.gameDao.addGame(game: testGame2);
|
||||
|
||||
var hasWinner = await database.gameDao.hasWinner(gameId: testGame2.id);
|
||||
expect(hasWinner, true);
|
||||
|
||||
await database.gameDao.removeWinner(gameId: testGame2.id);
|
||||
|
||||
hasWinner = await database.gameDao.hasWinner(gameId: testGame2.id);
|
||||
expect(hasWinner, false);
|
||||
|
||||
final removedWinner = await database.gameDao.getWinner(
|
||||
gameId: testGame2.id,
|
||||
);
|
||||
|
||||
expect(removedWinner, null);
|
||||
});
|
||||
|
||||
test('Renaming a game works correctly', () async {
|
||||
await database.gameDao.addGame(game: testGame1);
|
||||
|
||||
var fetchedGame = await database.gameDao.getGameById(
|
||||
gameId: testGame1.id,
|
||||
);
|
||||
expect(fetchedGame.name, testGame1.name);
|
||||
|
||||
const newName = 'Updated Game Name';
|
||||
await database.gameDao.updateGameName(
|
||||
gameId: testGame1.id,
|
||||
newName: newName,
|
||||
);
|
||||
|
||||
fetchedGame = await database.gameDao.getGameById(gameId: testGame1.id);
|
||||
expect(fetchedGame.name, newName);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -14,7 +14,8 @@ void main() {
|
||||
late Player testPlayer3;
|
||||
late Player testPlayer4;
|
||||
late Player testPlayer5;
|
||||
late Group testgroup;
|
||||
late Group testGroup1;
|
||||
late Group testGroup2;
|
||||
late Game testgameWithGroup;
|
||||
late Game testgameWithPlayers;
|
||||
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
||||
@@ -35,15 +36,19 @@ void main() {
|
||||
testPlayer3 = Player(name: 'Charlie');
|
||||
testPlayer4 = Player(name: 'Diana');
|
||||
testPlayer5 = Player(name: 'Eve');
|
||||
testgroup = Group(
|
||||
testGroup1 = Group(
|
||||
name: 'Test Group',
|
||||
members: [testPlayer1, testPlayer2, testPlayer3],
|
||||
);
|
||||
testGroup2 = Group(
|
||||
name: 'Test Group',
|
||||
members: [testPlayer3, testPlayer2],
|
||||
);
|
||||
testgameWithPlayers = Game(
|
||||
name: 'Test Game with Players',
|
||||
players: [testPlayer4, testPlayer5],
|
||||
);
|
||||
testgameWithGroup = Game(name: 'Test Game with Group', group: testgroup);
|
||||
testgameWithGroup = Game(name: 'Test Game with Group', group: testGroup1);
|
||||
});
|
||||
});
|
||||
tearDown(() async {
|
||||
@@ -52,7 +57,7 @@ void main() {
|
||||
group('Group-Game Tests', () {
|
||||
test('Game has group works correctly', () async {
|
||||
await database.gameDao.addGame(game: testgameWithPlayers);
|
||||
await database.groupDao.addGroup(group: testgroup);
|
||||
await database.groupDao.addGroup(group: testGroup1);
|
||||
|
||||
var gameHasGroup = await database.groupGameDao.gameHasGroup(
|
||||
gameId: testgameWithPlayers.id,
|
||||
@@ -61,8 +66,8 @@ void main() {
|
||||
expect(gameHasGroup, false);
|
||||
|
||||
await database.groupGameDao.addGroupToGame(
|
||||
testgameWithPlayers.id,
|
||||
testgroup.id,
|
||||
gameId: testgameWithPlayers.id,
|
||||
groupId: testGroup1.id,
|
||||
);
|
||||
|
||||
gameHasGroup = await database.groupGameDao.gameHasGroup(
|
||||
@@ -74,15 +79,15 @@ void main() {
|
||||
|
||||
test('Adding a group to a game works correctly', () async {
|
||||
await database.gameDao.addGame(game: testgameWithPlayers);
|
||||
await database.groupDao.addGroup(group: testgroup);
|
||||
await database.groupDao.addGroup(group: testGroup1);
|
||||
await database.groupGameDao.addGroupToGame(
|
||||
testgameWithPlayers.id,
|
||||
testgroup.id,
|
||||
gameId: testgameWithPlayers.id,
|
||||
groupId: testGroup1.id,
|
||||
);
|
||||
|
||||
var groupAdded = await database.groupGameDao.isGroupInGame(
|
||||
gameId: testgameWithPlayers.id,
|
||||
groupId: testgroup.id,
|
||||
groupId: testGroup1.id,
|
||||
);
|
||||
expect(groupAdded, true);
|
||||
|
||||
@@ -120,14 +125,55 @@ void main() {
|
||||
fail('Group should not be null');
|
||||
}
|
||||
|
||||
expect(group.id, testgroup.id);
|
||||
expect(group.name, testgroup.name);
|
||||
expect(group.createdAt, testgroup.createdAt);
|
||||
expect(group.members.length, testgroup.members.length);
|
||||
expect(group.id, testGroup1.id);
|
||||
expect(group.name, testGroup1.name);
|
||||
expect(group.createdAt, testGroup1.createdAt);
|
||||
expect(group.members.length, testGroup1.members.length);
|
||||
for (int i = 0; i < group.members.length; i++) {
|
||||
expect(group.members[i].id, testgroup.members[i].id);
|
||||
expect(group.members[i].name, testgroup.members[i].name);
|
||||
expect(group.members[i].createdAt, testgroup.members[i].createdAt);
|
||||
expect(group.members[i].id, testGroup1.members[i].id);
|
||||
expect(group.members[i].name, testGroup1.members[i].name);
|
||||
expect(group.members[i].createdAt, testGroup1.members[i].createdAt);
|
||||
}
|
||||
});
|
||||
|
||||
test('Updating the group of a game works correctly', () async {
|
||||
await database.gameDao.addGame(game: testgameWithGroup);
|
||||
|
||||
var group = await database.groupGameDao.getGroupOfGame(
|
||||
gameId: testgameWithGroup.id,
|
||||
);
|
||||
|
||||
if (group == null) {
|
||||
fail('Initial group should not be null');
|
||||
} else {
|
||||
expect(group.id, testGroup1.id);
|
||||
expect(group.name, testGroup1.name);
|
||||
expect(group.createdAt, testGroup1.createdAt);
|
||||
expect(group.members.length, testGroup1.members.length);
|
||||
}
|
||||
|
||||
await database.groupDao.addGroup(group: testGroup2);
|
||||
await database.groupGameDao.updateGroupOfGame(
|
||||
gameId: testgameWithGroup.id,
|
||||
newGroupId: testGroup2.id,
|
||||
);
|
||||
|
||||
group = await database.groupGameDao.getGroupOfGame(
|
||||
gameId: testgameWithGroup.id,
|
||||
);
|
||||
|
||||
if (group == null) {
|
||||
fail('Updated group should not be null');
|
||||
} else {
|
||||
expect(group.id, testGroup2.id);
|
||||
expect(group.name, testGroup2.name);
|
||||
expect(group.createdAt, testGroup2.createdAt);
|
||||
expect(group.members.length, testGroup2.members.length);
|
||||
for (int i = 0; i < group.members.length; i++) {
|
||||
expect(group.members[i].id, testGroup2.members[i].id);
|
||||
expect(group.members[i].name, testGroup2.members[i].name);
|
||||
expect(group.members[i].createdAt, testGroup2.members[i].createdAt);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -81,7 +81,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('Adding and fetching multiple groups works correctly', () async {
|
||||
await database.groupDao.addGroups(
|
||||
await database.groupDao.addGroupsAsList(
|
||||
groups: [testGroup1, testGroup2, testGroup3, testGroup4],
|
||||
);
|
||||
|
||||
|
||||
@@ -136,5 +136,48 @@ void main() {
|
||||
expect(players[i].createdAt, testGameOnlyPlayers.players![i].createdAt);
|
||||
}
|
||||
});
|
||||
|
||||
test('Updating the games players works coreclty', () async {
|
||||
|
sneeex marked this conversation as resolved
sneeex
commented
*correctly *correctly
flixcoo
commented
digga warum mache ich das immer falsch omg digga warum mache ich das immer falsch omg
|
||||
await database.gameDao.addGame(game: testGameOnlyPlayers);
|
||||
|
||||
final newPlayers = [testPlayer1, testPlayer2, testPlayer4];
|
||||
await database.playerDao.addPlayersAsList(players: newPlayers);
|
||||
|
||||
// First, remove all existing players
|
||||
final existingPlayers = await database.playerGameDao.getPlayersOfGame(
|
||||
gameId: testGameOnlyPlayers.id,
|
||||
);
|
||||
|
||||
if (existingPlayers == null || existingPlayers.isEmpty) {
|
||||
fail('Existing players should not be null or empty');
|
||||
}
|
||||
|
||||
await database.playerGameDao.updatePlayersFromGame(
|
||||
gameId: testGameOnlyPlayers.id,
|
||||
newPlayer: newPlayers,
|
||||
);
|
||||
|
||||
final updatedPlayers = await database.playerGameDao.getPlayersOfGame(
|
||||
gameId: testGameOnlyPlayers.id,
|
||||
);
|
||||
|
||||
if (updatedPlayers == null) {
|
||||
fail('Updated players should not be null');
|
||||
}
|
||||
|
||||
expect(updatedPlayers.length, newPlayers.length);
|
||||
|
||||
/// Create a map of new players for easy lookup
|
||||
final testPlayers = {for (var p in newPlayers) p.id: p};
|
||||
|
||||
/// Verify each updated player matches the new players
|
||||
for (final player in updatedPlayers) {
|
||||
final testPlayer = testPlayers[player.id]!;
|
||||
|
||||
expect(player.id, testPlayer.id);
|
||||
expect(player.name, testPlayer.name);
|
||||
expect(player.createdAt, testPlayer.createdAt);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ void main() {
|
||||
});
|
||||
|
||||
test('Adding and fetching multiple players works correctly', () async {
|
||||
await database.playerDao.addPlayers(
|
||||
await database.playerDao.addPlayersAsList(
|
||||
players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4],
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user
sind hier nicht addGroupToGame und updateGroupOfGame redundant? Zumindest steht bei addGroupToGame ja, dass es replaced wird, wenn's existiert