diff --git a/assets/schema.json b/assets/schema.json index b3a8a2c..8021012 100644 --- a/assets/schema.json +++ b/assets/schema.json @@ -15,16 +15,91 @@ }, "name": { "type": "string" + }, + "description": { + "type": "string" } }, "required": [ "id", "createdAt", - "name" + "name", + "description" + ] + } + }, + "games": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "name": { + "type": "string" + }, + "ruleset": { + "type": "string" + }, + "description": { + "type": "string" + }, + "color": { + "type": "string" + }, + "icon": { + "type": "string" + } + }, + "required": [ + "id", + "createdAt", + "name", + "ruleset", + "description", + "color", + "icon" ] } }, "groups": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "createdAt": { + "type": "string" + }, + "description": { + "type": "string" + }, + "memberIds": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ + "id", + "name", + "createdAt", + "description", + "memberIds" + ] + } + }, + "teams": { "type": "array", "items": { "type": "object", @@ -67,11 +142,14 @@ "createdAt": { "type": "string" }, + "endedAt": { + "type": ["string", "null"] + }, + "gameId": { + "type": "string" + }, "groupId": { - "anyOf": [ - {"type": "string"}, - {"type": "null"} - ] + "type": ["string", "null"] }, "playerIds": { "type": "array", @@ -79,26 +157,26 @@ "type": "string" } }, - "winnerId": { - "anyOf": [ - {"type": "string"}, - {"type": "null"} - ] + "notes": { + "type": "string" } }, "required": [ "id", "name", "createdAt", - "groupId", - "playerIds" + "gameId", + "playerIds", + "notes" ] } } }, "required": [ "players", + "games", "groups", + "teams", "matches" ] } \ No newline at end of file diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 53288e3..d3e0610 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -29,24 +29,38 @@ enum ImportResult { /// - [ExportResult.unknownException]: An exception occurred during export. enum ExportResult { success, canceled, unknownException } -/// Different rulesets available for matches -/// - [Ruleset.singleWinner]: The match is won by a single player -/// - [Ruleset.singleLoser]: The match is lost by a single player -/// - [Ruleset.mostPoints]: The player with the most points wins. -/// - [Ruleset.leastPoints]: The player with the fewest points wins. -enum Ruleset { singleWinner, singleLoser, mostPoints, leastPoints } +/// Different rulesets available for games +/// - [Ruleset.highestScore]: The player with the highest score wins. +/// - [Ruleset.lowestScore]: The player with the lowest score wins. +/// - [Ruleset.singleWinner]: The match is won by a single player. +/// - [Ruleset.singleLoser]: The match has a single loser. +/// - [Ruleset.multipleWinners]: Multiple players can be winners. +enum Ruleset { highestScore, lowestScore, singleWinner, singleLoser, multipleWinners } + +/// Different colors available for games +/// - [GameColor.red]: Red color +/// - [GameColor.blue]: Blue color +/// - [GameColor.green]: Green color +/// - [GameColor.yellow]: Yellow color +/// - [GameColor.purple]: Purple color +/// - [GameColor.orange]: Orange color +/// - [GameColor.pink]: Pink color +/// - [GameColor.teal]: Teal color +enum GameColor { red, blue, green, yellow, purple, orange, pink, teal } /// Translates a [Ruleset] enum value to its corresponding localized string. String translateRulesetToString(Ruleset ruleset, BuildContext context) { final loc = AppLocalizations.of(context); switch (ruleset) { + case Ruleset.highestScore: + return loc.highest_score; + case Ruleset.lowestScore: + return loc.lowest_score; case Ruleset.singleWinner: return loc.single_winner; case Ruleset.singleLoser: return loc.single_loser; - case Ruleset.mostPoints: - return loc.most_points; - case Ruleset.leastPoints: - return loc.least_points; + case Ruleset.multipleWinners: + return loc.multiple_winners; } } diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart new file mode 100644 index 0000000..8e658ed --- /dev/null +++ b/lib/data/dao/game_dao.dart @@ -0,0 +1,166 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/dto/game.dart'; +import 'package:tallee/core/enums.dart'; + +part 'game_dao.g.dart'; + +@DriftAccessor(tables: [GameTable]) +class GameDao extends DatabaseAccessor with _$GameDaoMixin { + GameDao(super.db); + + /// Retrieves all games from the database. + Future> getAllGames() async { + final query = select(gameTable); + final result = await query.get(); + return result + .map( + (row) => Game( + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: GameColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, + ), + ) + .toList(); + } + + /// Retrieves a [Game] by its [gameId]. + Future getGameById({required String gameId}) async { + final query = select(gameTable)..where((g) => g.id.equals(gameId)); + final result = await query.getSingle(); + return Game( + id: result.id, + name: result.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == result.ruleset), + description: result.description, + color: GameColor.values.firstWhere((e) => e.name == result.color), + icon: result.icon, + createdAt: result.createdAt, + ); + } + + /// Adds a new [game] to the database. + /// If a game with the same ID already exists, no action is taken. + /// Returns `true` if the game was added, `false` otherwise. + Future addGame({required Game game}) async { + if (!await gameExists(gameId: game.id)) { + await into(gameTable).insert( + GameTableCompanion.insert( + id: game.id, + name: game.name, + ruleset: game.ruleset.name, + description: game.description, + color: game.color.name, + icon: game.icon, + createdAt: game.createdAt, + ), + mode: InsertMode.insertOrReplace, + ); + return true; + } + return false; + } + + /// Adds multiple [games] to the database in a batch operation. + /// Uses insertOrIgnore to avoid overwriting existing games. + Future addGamesAsList({required List games}) async { + if (games.isEmpty) return false; + + await db.batch( + (b) => b.insertAll( + gameTable, + games + .map( + (game) => GameTableCompanion.insert( + id: game.id, + name: game.name, + ruleset: game.ruleset.name, + description: game.description, + color: game.color.name, + icon: game.icon, + createdAt: game.createdAt, + ), + ) + .toList(), + mode: InsertMode.insertOrIgnore, + ), + ); + + return true; + } + + /// Deletes the game with the given [gameId] from the database. + /// Returns `true` if the game was deleted, `false` if the game did not exist. + Future deleteGame({required String gameId}) async { + final query = delete(gameTable)..where((g) => g.id.equals(gameId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } + + /// Checks if a game with the given [gameId] exists in the database. + /// Returns `true` if the game exists, `false` otherwise. + Future gameExists({required String gameId}) async { + final query = select(gameTable)..where((g) => g.id.equals(gameId)); + final result = await query.getSingleOrNull(); + return result != null; + } + + /// Updates the name of the game with the given [gameId] to [newName]. + Future updateGameName({required String gameId, required String newName}) async { + await (update( + gameTable, + )..where((g) => g.id.equals(gameId))).write(GameTableCompanion(name: Value(newName))); + } + + /// Updates the ruleset of the game with the given [gameId]. + Future updateGameRuleset({required String gameId, required Ruleset newRuleset}) async { + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + GameTableCompanion(ruleset: Value(newRuleset.name)), + ); + } + + /// Updates the description of the game with the given [gameId]. + Future updateGameDescription({ + required String gameId, + required String newDescription, + }) async { + await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + GameTableCompanion(description: Value(newDescription)), + ); + } + + /// Updates the color of the game with the given [gameId]. + Future updateGameColor({required String gameId, required GameColor newColor}) async { + await (update( + gameTable, + )..where((g) => g.id.equals(gameId))).write(GameTableCompanion(color: Value(newColor.name))); + } + + /// Updates the icon of the game with the given [gameId]. + Future updateGameIcon({required String gameId, required String newIcon}) async { + await (update( + gameTable, + )..where((g) => g.id.equals(gameId))).write(GameTableCompanion(icon: Value(newIcon))); + } + + /// Retrieves the total count of games in the database. + Future getGameCount() async { + final count = await (selectOnly( + gameTable, + )..addColumns([gameTable.id.count()])).map((row) => row.read(gameTable.id.count())).getSingle(); + return count ?? 0; + } + + /// Deletes all games from the database. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future deleteAllGames() async { + final query = delete(gameTable); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } +} diff --git a/lib/data/dao/game_dao.g.dart b/lib/data/dao/game_dao.g.dart new file mode 100644 index 0000000..b5a29fe --- /dev/null +++ b/lib/data/dao/game_dao.g.dart @@ -0,0 +1,8 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'game_dao.dart'; + +// ignore_for_file: type=lint +mixin _$GameDaoMixin on DatabaseAccessor { + $GameTableTable get gameTable => attachedDatabase.gameTable; +} diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index 086cb2d..c5abfd5 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -23,6 +23,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { return Group( id: groupData.id, name: groupData.name, + description: groupData.description, members: members, createdAt: groupData.createdAt, ); @@ -42,6 +43,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { return Group( id: result.id, name: result.name, + description: result.description, members: members, createdAt: result.createdAt, ); @@ -56,6 +58,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { GroupTableCompanion.insert( id: group.id, name: group.name, + description: group.description, createdAt: group.createdAt, ), mode: InsertMode.insertOrReplace, @@ -105,6 +108,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { (group) => GroupTableCompanion.insert( id: group.id, name: group.name, + description: group.description, createdAt: group.createdAt, ), ) @@ -132,6 +136,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { (p) => PlayerTableCompanion.insert( id: p.id, name: p.name, + description: p.description, createdAt: p.createdAt, ), ) @@ -176,7 +181,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Updates the name of the group with the given [id] to [newName]. /// Returns `true` if more than 0 rows were affected, otherwise `false`. - Future updateGroupname({ + Future updateGroupName({ required String groupId, required String newName, }) async { @@ -187,6 +192,21 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { return rowsAffected > 0; } + /// Updates the description of the group with the given [groupId] to [newDescription]. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateGroupDescription({ + required String groupId, + required String newDescription, + }) async { + final rowsAffected = + await (update(groupTable)..where((g) => g.id.equals(groupId))).write( + GroupTableCompanion(description: Value(newDescription)), + ); + return rowsAffected > 0; + } + + + /// Retrieves the number of groups in the database. Future getGroupCount() async { final count = @@ -211,4 +231,44 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final rowsAffected = await query.go(); return rowsAffected > 0; } + + /// Replaces all players in a group with the provided list of players. + /// Removes all existing players from the group and adds the new players. + /// Also adds any new players to the player table if they don't exist. + Future replaceGroupPlayers({ + required String groupId, + required List newPlayers, + }) async { + await db.transaction(() async { + // Remove all existing players from the group + final deleteQuery = delete(db.playerGroupTable) + ..where((p) => p.groupId.equals(groupId)); + await deleteQuery.go(); + + // Add new players to the player table if they don't exist + await Future.wait( + newPlayers.map((player) async { + if (!await db.playerDao.playerExists(playerId: player.id)) { + await db.playerDao.addPlayer(player: player); + } + }), + ); + + // Add the new players to the group + await db.batch( + (b) => b.insertAll( + db.playerGroupTable, + newPlayers + .map( + (player) => PlayerGroupTableCompanion.insert( + playerId: player.id, + groupId: groupId, + ), + ) + .toList(), + mode: InsertMode.insertOrReplace, + ), + ); + }); + } } diff --git a/lib/data/dao/group_match_dao.dart b/lib/data/dao/group_match_dao.dart deleted file mode 100644 index de2eaf1..0000000 --- a/lib/data/dao/group_match_dao.dart +++ /dev/null @@ -1,98 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/db/tables/group_match_table.dart'; -import 'package:tallee/data/dto/group.dart'; - -part 'group_match_dao.g.dart'; - -@DriftAccessor(tables: [GroupMatchTable]) -class GroupMatchDao extends DatabaseAccessor - with _$GroupMatchDaoMixin { - GroupMatchDao(super.db); - - /// Associates a group with a match by inserting a record into the - /// [GroupMatchTable]. - Future addGroupToMatch({ - required String matchId, - required String groupId, - }) async { - if (await matchHasGroup(matchId: matchId)) { - throw Exception('Match already has a group'); - } - await into(groupMatchTable).insert( - GroupMatchTableCompanion.insert(groupId: groupId, matchId: matchId), - mode: InsertMode.insertOrIgnore, - ); - } - - /// Retrieves the [Group] associated with the given [matchId]. - /// Returns `null` if no group is found. - Future getGroupOfMatch({required String matchId}) async { - final result = await (select( - groupMatchTable, - )..where((g) => g.matchId.equals(matchId))).getSingleOrNull(); - - if (result == null) { - return null; - } - - final group = await db.groupDao.getGroupById(groupId: result.groupId); - return group; - } - - /// Checks if there is a group associated with the given [matchId]. - /// Returns `true` if there is a group, otherwise `false`. - Future matchHasGroup({required String matchId}) async { - final count = - await (selectOnly(groupMatchTable) - ..where(groupMatchTable.matchId.equals(matchId)) - ..addColumns([groupMatchTable.groupId.count()])) - .map((row) => row.read(groupMatchTable.groupId.count())) - .getSingle(); - return (count ?? 0) > 0; - } - - /// Checks if a specific group is associated with a specific match. - /// Returns `true` if the group is in the match, otherwise `false`. - Future isGroupInMatch({ - required String matchId, - required String groupId, - }) async { - final count = - await (selectOnly(groupMatchTable) - ..where( - groupMatchTable.matchId.equals(matchId) & - groupMatchTable.groupId.equals(groupId), - ) - ..addColumns([groupMatchTable.groupId.count()])) - .map((row) => row.read(groupMatchTable.groupId.count())) - .getSingle(); - return (count ?? 0) > 0; - } - - /// Removes the association of a group from a match based on [groupId] and - /// [matchId]. - /// Returns `true` if more than 0 rows were affected, otherwise `false`. - Future removeGroupFromMatch({ - required String matchId, - required String groupId, - }) async { - final query = delete(groupMatchTable) - ..where((g) => g.matchId.equals(matchId) & g.groupId.equals(groupId)); - final rowsAffected = await query.go(); - return rowsAffected > 0; - } - - /// Updates the group associated with a match to [newGroupId] based on - /// [matchId]. - /// Returns `true` if more than 0 rows were affected, otherwise `false`. - Future updateGroupOfMatch({ - required String matchId, - required String newGroupId, - }) async { - final updatedRows = - await (update(groupMatchTable)..where((g) => g.matchId.equals(matchId))) - .write(GroupMatchTableCompanion(groupId: Value(newGroupId))); - return updatedRows > 0; - } -} diff --git a/lib/data/dao/group_match_dao.g.dart b/lib/data/dao/group_match_dao.g.dart deleted file mode 100644 index 5cc0b82..0000000 --- a/lib/data/dao/group_match_dao.g.dart +++ /dev/null @@ -1,10 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'group_match_dao.dart'; - -// ignore_for_file: type=lint -mixin _$GroupMatchDaoMixin on DatabaseAccessor { - $GroupTableTable get groupTable => attachedDatabase.groupTable; - $MatchTableTable get matchTable => attachedDatabase.matchTable; - $GroupMatchTableTable get groupMatchTable => attachedDatabase.groupMatchTable; -} diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index cc3a37f..5726df5 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -1,13 +1,17 @@ import 'package:drift/drift.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/match_table.dart'; +import 'package:tallee/data/db/tables/player_match_table.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; part 'match_dao.g.dart'; -@DriftAccessor(tables: [MatchTable]) +@DriftAccessor(tables: [MatchTable, GameTable, GroupTable, PlayerMatchTable]) class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { MatchDao(super.db); @@ -18,20 +22,23 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return Future.wait( result.map((row) async { - final group = await db.groupMatchDao.getGroupOfMatch(matchId: row.id); + final game = await db.gameDao.getGameById(gameId: row.gameId); + Group? group; + if (row.groupId != null) { + group = await db.groupDao.getGroupById(groupId: row.groupId!); + } final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, - ); - final winner = row.winnerId != null - ? await db.playerDao.getPlayerById(playerId: row.winnerId!) - : null; + ) ?? []; return Match( id: row.id, - name: row.name, + name: row.name ?? '', + game: game, group: group, players: players, + notes: row.notes ?? '', createdAt: row.createdAt, - winner: winner, + endedAt: row.endedAt, ); }), ); @@ -42,114 +49,134 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { final query = select(matchTable)..where((g) => g.id.equals(matchId)); final result = await query.getSingle(); - List? players; - if (await db.playerMatchDao.matchHasPlayers(matchId: matchId)) { - players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); - } + final game = await db.gameDao.getGameById(gameId: result.gameId); + Group? group; - if (await db.groupMatchDao.matchHasGroup(matchId: matchId)) { - group = await db.groupMatchDao.getGroupOfMatch(matchId: matchId); - } - Player? winner; - if (result.winnerId != null) { - winner = await db.playerDao.getPlayerById(playerId: result.winnerId!); + if (result.groupId != null) { + group = await db.groupDao.getGroupById(groupId: result.groupId!); } + final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; + return Match( id: result.id, - name: result.name, - players: players, + name: result.name ?? '', + game: game, group: group, - winner: winner, + players: players, + notes: result.notes ?? '', createdAt: result.createdAt, + endedAt: result.endedAt, ); } - /// Adds a new [Match] to the database. Also adds players and group - /// associations. This method assumes that the players and groups added to - /// this match are already present in the database. + /// Adds a new [Match] to the database. Also adds players associations. + /// This method assumes that the game and group (if any) are already present + /// in the database. Future addMatch({required Match match}) async { await db.transaction(() async { await into(matchTable).insert( MatchTableCompanion.insert( id: match.id, - name: match.name, - winnerId: Value(match.winner?.id), + gameId: match.game.id, + groupId: Value(match.group?.id), + name: Value(match.name), + notes: Value(match.notes), createdAt: match.createdAt, + endedAt: Value(match.endedAt), ), mode: InsertMode.insertOrReplace, ); - if (match.players != null) { - for (final p in match.players ?? []) { - await db.playerMatchDao.addPlayerToMatch( - matchId: match.id, - playerId: p.id, - ); - } - } - - if (match.group != null) { - await db.groupMatchDao.addGroupToMatch( + for (final p in match.players) { + await db.playerMatchDao.addPlayerToMatch( matchId: match.id, - groupId: match.group!.id, + playerId: p.id, ); } }); } - /// Adds multiple [Match]s to the database in a batch operation. + /// Adds multiple [Match]es to the database in a batch operation. /// Also adds associated players and groups if they exist. /// If the [matches] list is empty, the method returns immediately. - /// This Method should only be used to import matches from a different device. + /// This method should only be used to import matches from a different device. Future addMatchAsList({required List matches}) async { if (matches.isEmpty) return; await db.transaction(() async { - // Add all matches in batch - await db.batch( - (b) => b.insertAll( - matchTable, - matches - .map( - (match) => MatchTableCompanion.insert( - id: match.id, - name: match.name, - createdAt: match.createdAt, - winnerId: Value(match.winner?.id), - ), - ) - .toList(), - mode: InsertMode.insertOrReplace, - ), - ); + // Add all games first (deduplicated) + final uniqueGames = {}; + for (final match in matches) { + uniqueGames[match.game.id] = match.game; + } + + if (uniqueGames.isNotEmpty) { + await db.batch( + (b) => b.insertAll( + db.gameTable, + uniqueGames.values + .map( + (game) => GameTableCompanion.insert( + id: game.id, + name: game.name, + ruleset: game.ruleset.name, + description: game.description, + color: game.color.name, + icon: game.icon, + createdAt: game.createdAt, + ), + ) + .toList(), + mode: InsertMode.insertOrIgnore, + ), + ); + } // Add all groups of the matches in batch - // Using insertOrIgnore to avoid overwriting existing groups (which would - // trigger cascade deletes on player_group associations) await db.batch( - (b) => b.insertAll( + (b) => b.insertAll( db.groupTable, matches .where((match) => match.group != null) .map( - (matches) => GroupTableCompanion.insert( - id: matches.group!.id, - name: matches.group!.name, - createdAt: matches.group!.createdAt, - ), - ) + (match) => GroupTableCompanion.insert( + id: match.group!.id, + name: match.group!.name, + description: match.group!.description, + createdAt: match.group!.createdAt, + ), + ) .toList(), mode: InsertMode.insertOrIgnore, ), ); + // Add all matches in batch + await db.batch( + (b) => b.insertAll( + matchTable, + matches + .map( + (match) => MatchTableCompanion.insert( + id: match.id, + gameId: match.game.id, + groupId: Value(match.group?.id), + name: Value(match.name), + notes: Value(match.notes), + createdAt: match.createdAt, + endedAt: Value(match.endedAt), + ), + ) + .toList(), + mode: InsertMode.insertOrReplace, + ), + ); + // Add all players of the matches in batch (unique) final uniquePlayers = {}; for (final match in matches) { - if (match.players != null) { - for (final p in match.players!) { - uniquePlayers[p.id] = p; - } + for (final p in match.players) { + uniquePlayers[p.id] = p; } // Also include members of groups if (match.group != null) { @@ -160,19 +187,18 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { } if (uniquePlayers.isNotEmpty) { - // Using insertOrIgnore to avoid triggering cascade deletes on - // player_group/player_match associations when players already exist await db.batch( - (b) => b.insertAll( + (b) => b.insertAll( db.playerTable, uniquePlayers.values .map( (p) => PlayerTableCompanion.insert( - id: p.id, - name: p.name, - createdAt: p.createdAt, - ), - ) + id: p.id, + name: p.name, + description: p.description, + createdAt: p.createdAt, + ), + ) .toList(), mode: InsertMode.insertOrIgnore, ), @@ -182,17 +208,16 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { // Add all player-match associations in batch await db.batch((b) { for (final match in matches) { - if (match.players != null) { - for (final p in match.players ?? []) { - b.insert( - db.playerMatchTable, - PlayerMatchTableCompanion.insert( - matchId: match.id, - playerId: p.id, - ), - mode: InsertMode.insertOrIgnore, - ); - } + for (final p in match.players) { + b.insert( + db.playerMatchTable, + PlayerMatchTableCompanion.insert( + matchId: match.id, + playerId: p.id, + score: 0, + ), + mode: InsertMode.insertOrIgnore, + ); } } }); @@ -214,22 +239,6 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { } } }); - - // Add all group-match associations in batch - await db.batch((b) { - for (final match in matches) { - if (match.group != null) { - b.insert( - db.groupMatchTable, - GroupMatchTableCompanion.insert( - matchId: match.id, - groupId: match.group!.id, - ), - mode: InsertMode.insertOrIgnore, - ); - } - } - }); }); } @@ -244,9 +253,9 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Retrieves the number of matches in the database. Future getMatchCount() async { final count = - await (selectOnly(matchTable)..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) - .getSingle(); + await (selectOnly(matchTable)..addColumns([matchTable.id.count()])) + .map((row) => row.read(matchTable.id.count())) + .getSingle(); return count ?? 0; } @@ -266,52 +275,20 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return rowsAffected > 0; } - /// Sets the winner of the match with the given [matchId] to the player with - /// the given [winnerId]. + /// Updates the notes of the match with the given [matchId]. /// Returns `true` if more than 0 rows were affected, otherwise `false`. - Future setWinner({ + Future updateMatchNotes({ required String matchId, - required String winnerId, + required String? notes, }) async { final query = update(matchTable)..where((g) => g.id.equals(matchId)); final rowsAffected = await query.write( - MatchTableCompanion(winnerId: Value(winnerId)), + MatchTableCompanion(notes: Value(notes)), ); return rowsAffected > 0; } - /// Retrieves the winner of the match with the given [matchId]. - /// Returns the [Player] who won the match, or `null` if no winner is set. - Future getWinner({required String matchId}) async { - final query = select(matchTable)..where((g) => g.id.equals(matchId)); - 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 match with the given [matchId]. - /// Returns `true` if more than 0 rows were affected, otherwise `false`. - Future removeWinner({required String matchId}) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); - final rowsAffected = await query.write( - const MatchTableCompanion(winnerId: Value(null)), - ); - return rowsAffected > 0; - } - - /// Checks if the match with the given [matchId] has a winner set. - /// Returns `true` if a winner is set, otherwise `false`. - Future hasWinner({required String matchId}) async { - final query = select(matchTable) - ..where((g) => g.id.equals(matchId) & g.winnerId.isNotNull()); - final result = await query.getSingleOrNull(); - return result != null; - } - - /// Changes the title of the match with the given [matchId] to [newName]. + /// Changes the name of the match with the given [matchId] to [newName]. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future updateMatchName({ required String matchId, @@ -323,4 +300,174 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { ); return rowsAffected > 0; } -} + + /// Updates the game of the match with the given [matchId]. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateMatchGame({ + required String matchId, + required String gameId, + }) async { + final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final rowsAffected = await query.write( + MatchTableCompanion(gameId: Value(gameId)), + ); + return rowsAffected > 0; + } + + /// Updates the group of the match with the given [matchId]. + /// Pass null to remove the group association. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateMatchGroup({ + required String matchId, + required String? groupId, + }) async { + final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final rowsAffected = await query.write( + MatchTableCompanion(groupId: Value(groupId)), + ); + return rowsAffected > 0; + } + + /// Updates the createdAt timestamp of the match with the given [matchId]. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateMatchCreatedAt({ + required String matchId, + required DateTime createdAt, + }) async { + final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final rowsAffected = await query.write( + MatchTableCompanion(createdAt: Value(createdAt)), + ); + return rowsAffected > 0; + } + + /// Updates the endedAt timestamp of the match with the given [matchId]. + /// Pass null to remove the ended time (mark match as ongoing). + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateMatchEndedAt({ + required String matchId, + required DateTime? endedAt, + }) async { + final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final rowsAffected = await query.write( + MatchTableCompanion(endedAt: Value(endedAt)), + ); + return rowsAffected > 0; + } + + /// Replaces all players in a match with the provided list of players. + /// Removes all existing players from the match and adds the new players. + /// Also adds any new players to the player table if they don't exist. + Future replaceMatchPlayers({ + required String matchId, + required List newPlayers, + }) async { + await db.transaction(() async { + // Remove all existing players from the match + final deleteQuery = delete(db.playerMatchTable) + ..where((p) => p.matchId.equals(matchId)); + await deleteQuery.go(); + + // Add new players to the player table if they don't exist + await Future.wait( + newPlayers.map((player) async { + if (!await db.playerDao.playerExists(playerId: player.id)) { + await db.playerDao.addPlayer(player: player); + } + }), + ); + + // Add the new players to the match + await Future.wait( + newPlayers.map((player) => db.playerMatchDao.addPlayerToMatch( + matchId: matchId, + playerId: player.id, + )), + ); + }); + } + + // ============================================================ + // Winner methods - handle winner logic via player scores + // ============================================================ + + /// Checks if a match has a winner. + /// Returns true if any player in the match has their score set to 1. + Future hasWinner({required String matchId}) async { + final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; + + for (final player in players) { + final score = await db.playerMatchDao.getPlayerScore( + matchId: matchId, + playerId: player.id, + ); + if (score == 1) { + return true; + } + } + return false; + } + + /// Gets the winner of a match. + /// Returns the player with score 1, or null if no winner is set. + Future getWinner({required String matchId}) async { + final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; + + for (final player in players) { + final score = await db.playerMatchDao.getPlayerScore( + matchId: matchId, + playerId: player.id, + ); + if (score == 1) { + return player; + } + } + return null; + } + + /// Sets the winner of a match. + /// Sets all players' scores to 0, then sets the specified player's score to 1. + /// Returns `true` if the operation was successful, otherwise `false`. + Future setWinner({ + required String matchId, + required String winnerId, + }) async { + await db.transaction(() async { + final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; + + // Set all players' scores to 0 + for (final player in players) { + await db.playerMatchDao.updatePlayerScore( + matchId: matchId, + playerId: player.id, + newScore: 0, + ); + } + + // Set the winner's score to 1 + await db.playerMatchDao.updatePlayerScore( + matchId: matchId, + playerId: winnerId, + newScore: 1, + ); + }); + return true; + } + + /// Removes the winner of a match. + /// Sets the current winner's score to 0 (no winner). + /// Returns `true` if a winner was removed, otherwise `false`. + Future removeWinner({required String matchId}) async { + final winner = await getWinner(matchId: matchId); + if (winner == null) { + return false; + } + + final success = await db.playerMatchDao.updatePlayerScore( + matchId: matchId, + playerId: winner.id, + newScore: 0, + ); + return success; + } +} \ No newline at end of file diff --git a/lib/data/dao/match_dao.g.dart b/lib/data/dao/match_dao.g.dart index a9f6f4c..fa75fee 100644 --- a/lib/data/dao/match_dao.g.dart +++ b/lib/data/dao/match_dao.g.dart @@ -4,5 +4,11 @@ part of 'match_dao.dart'; // ignore_for_file: type=lint mixin _$MatchDaoMixin on DatabaseAccessor { + $GameTableTable get gameTable => attachedDatabase.gameTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; $MatchTableTable get matchTable => attachedDatabase.matchTable; + $PlayerTableTable get playerTable => attachedDatabase.playerTable; + $TeamTableTable get teamTable => attachedDatabase.teamTable; + $PlayerMatchTableTable get playerMatchTable => + attachedDatabase.playerMatchTable; } diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index 8ac21f6..2da9761 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -15,7 +15,12 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final result = await query.get(); return result .map( - (row) => Player(id: row.id, name: row.name, createdAt: row.createdAt), + (row) => Player( + id: row.id, + name: row.name, + description: row.description, + createdAt: row.createdAt, + ), ) .toList(); } @@ -27,6 +32,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return Player( id: result.id, name: result.name, + description: result.description, createdAt: result.createdAt, ); } @@ -40,6 +46,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { PlayerTableCompanion.insert( id: player.id, name: player.name, + description: player.description, createdAt: player.createdAt, ), mode: InsertMode.insertOrReplace, @@ -63,6 +70,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { (player) => PlayerTableCompanion.insert( id: player.id, name: player.name, + description: player.description, createdAt: player.createdAt, ), ) @@ -91,7 +99,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { } /// Updates the name of the player with the given [playerId] to [newName]. - Future updatePlayername({ + Future updatePlayerName({ required String playerId, required String newName, }) async { diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index 23da0c1..c3889c8 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -27,7 +27,7 @@ class PlayerGroupDao extends DatabaseAccessor } if (!await db.playerDao.playerExists(playerId: player.id)) { - db.playerDao.addPlayer(player: player); + await db.playerDao.addPlayer(player: player); } await into(playerGroupTable).insert( diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index 6700e85..48bf282 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -1,23 +1,31 @@ import 'package:drift/drift.dart'; import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/db/tables/player_match_table.dart'; +import 'package:tallee/data/db/tables/team_table.dart'; import 'package:tallee/data/dto/player.dart'; part 'player_match_dao.g.dart'; -@DriftAccessor(tables: [PlayerMatchTable]) +@DriftAccessor(tables: [PlayerMatchTable, TeamTable]) class PlayerMatchDao extends DatabaseAccessor with _$PlayerMatchDaoMixin { PlayerMatchDao(super.db); /// Associates a player with a match by inserting a record into the - /// [PlayerMatchTable]. + /// [PlayerMatchTable]. Optionally associates with a team and sets initial score. Future addPlayerToMatch({ required String matchId, required String playerId, + String? teamId, + int score = 0, }) async { await into(playerMatchTable).insert( - PlayerMatchTableCompanion.insert(playerId: playerId, matchId: matchId), + PlayerMatchTableCompanion.insert( + playerId: playerId, + matchId: matchId, + teamId: Value(teamId), + score: score, + ), mode: InsertMode.insertOrIgnore, ); } @@ -32,21 +40,65 @@ class PlayerMatchDao extends DatabaseAccessor if (result.isEmpty) return null; final futures = result.map( - (row) => db.playerDao.getPlayerById(playerId: row.playerId), + (row) => db.playerDao.getPlayerById(playerId: row.playerId), ); final players = await Future.wait(futures); return players; } + /// Retrieves a player's score for a specific match. + /// Returns null if the player is not in the match. + Future getPlayerScore({ + required String matchId, + required String playerId, + }) async { + final result = await (select(playerMatchTable) + ..where( + (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + )) + .getSingleOrNull(); + return result?.score; + } + + /// Updates the score for a player in a match. + /// Returns `true` if the update was successful, otherwise `false`. + Future updatePlayerScore({ + required String matchId, + required String playerId, + required int newScore, + }) async { + final rowsAffected = await (update(playerMatchTable) + ..where( + (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + )) + .write(PlayerMatchTableCompanion(score: Value(newScore))); + return rowsAffected > 0; + } + + /// Updates the team for a player in a match. + /// Returns `true` if the update was successful, otherwise `false`. + Future updatePlayerTeam({ + required String matchId, + required String playerId, + required String? teamId, + }) async { + final rowsAffected = await (update(playerMatchTable) + ..where( + (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + )) + .write(PlayerMatchTableCompanion(teamId: Value(teamId))); + return rowsAffected > 0; + } + /// Checks if there are any players associated with the given [matchId]. /// Returns `true` if there are players, otherwise `false`. Future matchHasPlayers({required String matchId}) async { final count = - await (selectOnly(playerMatchTable) - ..where(playerMatchTable.matchId.equals(matchId)) - ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) - .getSingle(); + await (selectOnly(playerMatchTable) + ..where(playerMatchTable.matchId.equals(matchId)) + ..addColumns([playerMatchTable.playerId.count()])) + .map((row) => row.read(playerMatchTable.playerId.count())) + .getSingle(); return (count ?? 0) > 0; } @@ -57,12 +109,12 @@ class PlayerMatchDao extends DatabaseAccessor required String playerId, }) async { final count = - await (selectOnly(playerMatchTable) - ..where(playerMatchTable.matchId.equals(matchId)) - ..where(playerMatchTable.playerId.equals(playerId)) - ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) - .getSingle(); + await (selectOnly(playerMatchTable) + ..where(playerMatchTable.matchId.equals(matchId)) + ..where(playerMatchTable.playerId.equals(playerId)) + ..addColumns([playerMatchTable.playerId.count()])) + .map((row) => row.read(playerMatchTable.playerId.count())) + .getSingle(); return (count ?? 0) > 0; } @@ -96,14 +148,14 @@ class PlayerMatchDao extends DatabaseAccessor final playersToAdd = newPlayerIdsSet.difference(currentPlayerIds); final playersToRemove = currentPlayerIds.difference(newPlayerIdsSet); - db.transaction(() async { + await db.transaction(() async { // Remove old players if (playersToRemove.isNotEmpty) { await (delete(playerMatchTable)..where( (pg) => - pg.matchId.equals(matchId) & - pg.playerId.isIn(playersToRemove.toList()), - )) + pg.matchId.equals(matchId) & + pg.playerId.isIn(playersToRemove.toList()), + )) .go(); } @@ -112,14 +164,15 @@ class PlayerMatchDao extends DatabaseAccessor final inserts = playersToAdd .map( (id) => PlayerMatchTableCompanion.insert( - playerId: id, - matchId: matchId, - ), - ) + playerId: id, + matchId: matchId, + score: 0, + ), + ) .toList(); await Future.wait( inserts.map( - (c) => into( + (c) => into( playerMatchTable, ).insert(c, mode: InsertMode.insertOrIgnore), ), @@ -127,4 +180,23 @@ class PlayerMatchDao extends DatabaseAccessor } }); } + + /// Retrieves all players in a specific team for a match. + Future> getPlayersInTeam({ + required String matchId, + required String teamId, + }) async { + final result = await (select(playerMatchTable) + ..where( + (p) => p.matchId.equals(matchId) & p.teamId.equals(teamId), + )) + .get(); + + if (result.isEmpty) return []; + + final futures = result.map( + (row) => db.playerDao.getPlayerById(playerId: row.playerId), + ); + return Future.wait(futures); + } } diff --git a/lib/data/dao/player_match_dao.g.dart b/lib/data/dao/player_match_dao.g.dart index bcc8ef7..4c8bcbe 100644 --- a/lib/data/dao/player_match_dao.g.dart +++ b/lib/data/dao/player_match_dao.g.dart @@ -5,7 +5,10 @@ part of 'player_match_dao.dart'; // ignore_for_file: type=lint mixin _$PlayerMatchDaoMixin on DatabaseAccessor { $PlayerTableTable get playerTable => attachedDatabase.playerTable; + $GameTableTable get gameTable => attachedDatabase.gameTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; $MatchTableTable get matchTable => attachedDatabase.matchTable; + $TeamTableTable get teamTable => attachedDatabase.teamTable; $PlayerMatchTableTable get playerMatchTable => attachedDatabase.playerMatchTable; } diff --git a/lib/data/dao/score_dao.dart b/lib/data/dao/score_dao.dart new file mode 100644 index 0000000..04e8fcf --- /dev/null +++ b/lib/data/dao/score_dao.dart @@ -0,0 +1,191 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/score_table.dart'; + +part 'score_dao.g.dart'; + +/// A data class representing a score entry. +class ScoreEntry { + final String playerId; + final String matchId; + final int roundNumber; + final int score; + final int change; + + ScoreEntry({ + required this.playerId, + required this.matchId, + required this.roundNumber, + required this.score, + required this.change, + }); +} + +@DriftAccessor(tables: [ScoreTable]) +class ScoreDao extends DatabaseAccessor with _$ScoreDaoMixin { + ScoreDao(super.db); + + /// Adds a score entry to the database. + Future addScore({ + required String playerId, + required String matchId, + required int roundNumber, + required int score, + required int change, + }) async { + await into(scoreTable).insert( + ScoreTableCompanion.insert( + playerId: playerId, + matchId: matchId, + roundNumber: roundNumber, + score: score, + change: change, + ), + mode: InsertMode.insertOrReplace, + ); + } + + /// Retrieves all scores for a specific match. + Future> getScoresForMatch({required String matchId}) async { + final query = select(scoreTable)..where((s) => s.matchId.equals(matchId)); + final result = await query.get(); + return result + .map( + (row) => ScoreEntry( + playerId: row.playerId, + matchId: row.matchId, + roundNumber: row.roundNumber, + score: row.score, + change: row.change, + ), + ) + .toList(); + } + + /// Retrieves all scores for a specific player in a match. + Future> getPlayerScoresInMatch({ + required String playerId, + required String matchId, + }) async { + final query = select(scoreTable) + ..where( + (s) => s.playerId.equals(playerId) & s.matchId.equals(matchId), + ) + ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); + final result = await query.get(); + return result + .map( + (row) => ScoreEntry( + playerId: row.playerId, + matchId: row.matchId, + roundNumber: row.roundNumber, + score: row.score, + change: row.change, + ), + ) + .toList(); + } + + /// Retrieves the score for a specific round. + Future getScoreForRound({ + required String playerId, + required String matchId, + required int roundNumber, + }) async { + final query = select(scoreTable) + ..where( + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(roundNumber), + ); + final result = await query.getSingleOrNull(); + if (result == null) return null; + return ScoreEntry( + playerId: result.playerId, + matchId: result.matchId, + roundNumber: result.roundNumber, + score: result.score, + change: result.change, + ); + } + + /// Updates a score entry. + Future updateScore({ + required String playerId, + required String matchId, + required int roundNumber, + required int newScore, + required int newChange, + }) async { + final rowsAffected = await (update(scoreTable) + ..where( + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(roundNumber), + )) + .write( + ScoreTableCompanion( + score: Value(newScore), + change: Value(newChange), + ), + ); + return rowsAffected > 0; + } + + /// Deletes a score entry. + Future deleteScore({ + required String playerId, + required String matchId, + required int roundNumber, + }) async { + final query = delete(scoreTable) + ..where( + (s) => + s.playerId.equals(playerId) & + s.matchId.equals(matchId) & + s.roundNumber.equals(roundNumber), + ); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } + + /// Deletes all scores for a specific match. + Future deleteScoresForMatch({required String matchId}) async { + final query = delete(scoreTable)..where((s) => s.matchId.equals(matchId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } + + /// Deletes all scores for a specific player. + Future deleteScoresForPlayer({required String playerId}) async { + final query = delete(scoreTable)..where((s) => s.playerId.equals(playerId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } + + /// Gets the latest round number for a match. + Future getLatestRoundNumber({required String matchId}) async { + final query = selectOnly(scoreTable) + ..where(scoreTable.matchId.equals(matchId)) + ..addColumns([scoreTable.roundNumber.max()]); + final result = await query.getSingle(); + return result.read(scoreTable.roundNumber.max()) ?? 0; + } + + /// Gets the total score for a player in a match (sum of all changes). + Future getTotalScoreForPlayer({ + required String playerId, + required String matchId, + }) async { + final scores = await getPlayerScoresInMatch( + playerId: playerId, + matchId: matchId, + ); + if (scores.isEmpty) return 0; + // Return the score from the latest round + return scores.last.score; + } +} + diff --git a/lib/data/dao/score_dao.g.dart b/lib/data/dao/score_dao.g.dart new file mode 100644 index 0000000..1f4e367 --- /dev/null +++ b/lib/data/dao/score_dao.g.dart @@ -0,0 +1,12 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'score_dao.dart'; + +// ignore_for_file: type=lint +mixin _$ScoreDaoMixin on DatabaseAccessor { + $PlayerTableTable get playerTable => attachedDatabase.playerTable; + $GameTableTable get gameTable => attachedDatabase.gameTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; + $MatchTableTable get matchTable => attachedDatabase.matchTable; + $ScoreTableTable get scoreTable => attachedDatabase.scoreTable; +} diff --git a/lib/data/dao/team_dao.dart b/lib/data/dao/team_dao.dart new file mode 100644 index 0000000..5c2aadb --- /dev/null +++ b/lib/data/dao/team_dao.dart @@ -0,0 +1,147 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/team_table.dart'; +import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/data/dto/team.dart'; + +part 'team_dao.g.dart'; + +@DriftAccessor(tables: [TeamTable]) +class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { + TeamDao(super.db); + + /// Retrieves all teams from the database. + /// Note: This returns teams without their members. Use getTeamById for full team data. + Future> getAllTeams() async { + final query = select(teamTable); + final result = await query.get(); + return Future.wait( + result.map((row) async { + final members = await _getTeamMembers(teamId: row.id); + return Team( + id: row.id, + name: row.name, + createdAt: row.createdAt, + members: members, + ); + }), + ); + } + + /// Retrieves a [Team] by its [teamId], including its members. + Future getTeamById({required String teamId}) async { + final query = select(teamTable)..where((t) => t.id.equals(teamId)); + final result = await query.getSingle(); + final members = await _getTeamMembers(teamId: teamId); + return Team( + id: result.id, + name: result.name, + createdAt: result.createdAt, + members: members, + ); + } + + /// Helper method to get team members from player_match_table. + /// This assumes team members are tracked via the player_match_table. + Future> _getTeamMembers({required String teamId}) async { + // Get all player_match entries with this teamId + final playerMatchQuery = select(db.playerMatchTable) + ..where((pm) => pm.teamId.equals(teamId)); + final playerMatches = await playerMatchQuery.get(); + + if (playerMatches.isEmpty) return []; + + // Get unique player IDs + final playerIds = playerMatches.map((pm) => pm.playerId).toSet(); + + // Fetch all players + final players = await Future.wait( + playerIds.map((id) => db.playerDao.getPlayerById(playerId: id)), + ); + return players; + } + + /// Adds a new [team] to the database. + /// Returns `true` if the team was added, `false` otherwise. + Future addTeam({required Team team}) async { + if (!await teamExists(teamId: team.id)) { + await into(teamTable).insert( + TeamTableCompanion.insert( + id: team.id, + name: team.name, + createdAt: team.createdAt, + ), + mode: InsertMode.insertOrReplace, + ); + return true; + } + return false; + } + + /// Adds multiple [teams] to the database in a batch operation. + Future addTeamsAsList({required List teams}) async { + if (teams.isEmpty) return false; + + await db.batch( + (b) => b.insertAll( + teamTable, + teams + .map( + (team) => TeamTableCompanion.insert( + id: team.id, + name: team.name, + createdAt: team.createdAt, + ), + ) + .toList(), + mode: InsertMode.insertOrIgnore, + ), + ); + + return true; + } + + /// Deletes the team with the given [teamId] from the database. + /// Returns `true` if the team was deleted, `false` otherwise. + Future deleteTeam({required String teamId}) async { + final query = delete(teamTable)..where((t) => t.id.equals(teamId)); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } + + /// Checks if a team with the given [teamId] exists in the database. + /// Returns `true` if the team exists, `false` otherwise. + Future teamExists({required String teamId}) async { + final query = select(teamTable)..where((t) => t.id.equals(teamId)); + final result = await query.getSingleOrNull(); + return result != null; + } + + /// Updates the name of the team with the given [teamId]. + Future updateTeamName({ + required String teamId, + required String newName, + }) async { + await (update(teamTable)..where((t) => t.id.equals(teamId))).write( + TeamTableCompanion(name: Value(newName)), + ); + } + + /// Retrieves the total count of teams in the database. + Future getTeamCount() async { + final count = + await (selectOnly(teamTable)..addColumns([teamTable.id.count()])) + .map((row) => row.read(teamTable.id.count())) + .getSingle(); + return count ?? 0; + } + + /// Deletes all teams from the database. + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future deleteAllTeams() async { + final query = delete(teamTable); + final rowsAffected = await query.go(); + return rowsAffected > 0; + } +} + diff --git a/lib/data/dao/team_dao.g.dart b/lib/data/dao/team_dao.g.dart new file mode 100644 index 0000000..1bf5b21 --- /dev/null +++ b/lib/data/dao/team_dao.g.dart @@ -0,0 +1,8 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'team_dao.dart'; + +// ignore_for_file: type=lint +mixin _$TeamDaoMixin on DatabaseAccessor { + $TeamTableTable get teamTable => attachedDatabase.teamTable; +} diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index 4c70b21..4ab053e 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -1,18 +1,22 @@ import 'package:drift/drift.dart'; import 'package:drift_flutter/drift_flutter.dart'; import 'package:path_provider/path_provider.dart'; +import 'package:tallee/data/dao/game_dao.dart'; import 'package:tallee/data/dao/group_dao.dart'; -import 'package:tallee/data/dao/group_match_dao.dart'; import 'package:tallee/data/dao/match_dao.dart'; import 'package:tallee/data/dao/player_dao.dart'; import 'package:tallee/data/dao/player_group_dao.dart'; import 'package:tallee/data/dao/player_match_dao.dart'; -import 'package:tallee/data/db/tables/group_match_table.dart'; +import 'package:tallee/data/dao/score_dao.dart'; +import 'package:tallee/data/dao/team_dao.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; import 'package:tallee/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_match_table.dart'; import 'package:tallee/data/db/tables/player_table.dart'; +import 'package:tallee/data/db/tables/score_table.dart'; +import 'package:tallee/data/db/tables/team_table.dart'; part 'database.g.dart'; @@ -23,7 +27,9 @@ part 'database.g.dart'; MatchTable, PlayerGroupTable, PlayerMatchTable, - GroupMatchTable, + GameTable, + TeamTable, + ScoreTable, ], daos: [ PlayerDao, @@ -31,7 +37,9 @@ part 'database.g.dart'; MatchDao, PlayerGroupDao, PlayerMatchDao, - GroupMatchDao, + GameDao, + ScoreDao, + TeamDao ], ) class AppDatabase extends _$AppDatabase { @@ -52,9 +60,7 @@ class AppDatabase extends _$AppDatabase { static QueryExecutor _openConnection() { return driftDatabase( name: 'gametracker_db', - native: const DriftNativeOptions( - databaseDirectory: getApplicationSupportDirectory, - ), + native: const DriftNativeOptions(databaseDirectory: getApplicationSupportDirectory), ); } } diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index 4fa56f9..fe14e93 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -27,6 +27,17 @@ class $PlayerTableTable extends PlayerTable type: DriftSqlType.string, requiredDuringInsert: true, ); + static const VerificationMeta _descriptionMeta = const VerificationMeta( + 'description', + ); + @override + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); static const VerificationMeta _createdAtMeta = const VerificationMeta( 'createdAt', ); @@ -39,7 +50,7 @@ class $PlayerTableTable extends PlayerTable requiredDuringInsert: true, ); @override - List get $columns => [id, name, createdAt]; + List get $columns => [id, name, description, createdAt]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -65,6 +76,17 @@ class $PlayerTableTable extends PlayerTable } else if (isInserting) { context.missing(_nameMeta); } + if (data.containsKey('description')) { + context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, + _descriptionMeta, + ), + ); + } else if (isInserting) { + context.missing(_descriptionMeta); + } if (data.containsKey('created_at')) { context.handle( _createdAtMeta, @@ -90,6 +112,10 @@ class $PlayerTableTable extends PlayerTable DriftSqlType.string, data['${effectivePrefix}name'], )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}created_at'], @@ -106,10 +132,12 @@ class $PlayerTableTable extends PlayerTable class PlayerTableData extends DataClass implements Insertable { final String id; final String name; + final String description; final DateTime createdAt; const PlayerTableData({ required this.id, required this.name, + required this.description, required this.createdAt, }); @override @@ -117,6 +145,7 @@ class PlayerTableData extends DataClass implements Insertable { final map = {}; map['id'] = Variable(id); map['name'] = Variable(name); + map['description'] = Variable(description); map['created_at'] = Variable(createdAt); return map; } @@ -125,6 +154,7 @@ class PlayerTableData extends DataClass implements Insertable { return PlayerTableCompanion( id: Value(id), name: Value(name), + description: Value(description), createdAt: Value(createdAt), ); } @@ -137,6 +167,7 @@ class PlayerTableData extends DataClass implements Insertable { return PlayerTableData( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), createdAt: serializer.fromJson(json['createdAt']), ); } @@ -146,20 +177,29 @@ class PlayerTableData extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'name': serializer.toJson(name), + 'description': serializer.toJson(description), 'createdAt': serializer.toJson(createdAt), }; } - PlayerTableData copyWith({String? id, String? name, DateTime? createdAt}) => - PlayerTableData( - id: id ?? this.id, - name: name ?? this.name, - createdAt: createdAt ?? this.createdAt, - ); + PlayerTableData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + }) => PlayerTableData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + ); PlayerTableData copyWithCompanion(PlayerTableCompanion data) { return PlayerTableData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @@ -169,50 +209,58 @@ class PlayerTableData extends DataClass implements Insertable { return (StringBuffer('PlayerTableData(') ..write('id: $id, ') ..write('name: $name, ') + ..write('description: $description, ') ..write('createdAt: $createdAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, name, createdAt); + int get hashCode => Object.hash(id, name, description, createdAt); @override bool operator ==(Object other) => identical(this, other) || (other is PlayerTableData && other.id == this.id && other.name == this.name && + other.description == this.description && other.createdAt == this.createdAt); } class PlayerTableCompanion extends UpdateCompanion { final Value id; final Value name; + final Value description; final Value createdAt; final Value rowid; const PlayerTableCompanion({ this.id = const Value.absent(), this.name = const Value.absent(), + this.description = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); PlayerTableCompanion.insert({ required String id, required String name, + required String description, required DateTime createdAt, this.rowid = const Value.absent(), }) : id = Value(id), name = Value(name), + description = Value(description), createdAt = Value(createdAt); static Insertable custom({ Expression? id, Expression? name, + Expression? description, Expression? createdAt, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, + if (description != null) 'description': description, if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); @@ -221,12 +269,14 @@ class PlayerTableCompanion extends UpdateCompanion { PlayerTableCompanion copyWith({ Value? id, Value? name, + Value? description, Value? createdAt, Value? rowid, }) { return PlayerTableCompanion( id: id ?? this.id, name: name ?? this.name, + description: description ?? this.description, createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); @@ -241,6 +291,9 @@ class PlayerTableCompanion extends UpdateCompanion { if (name.present) { map['name'] = Variable(name.value); } + if (description.present) { + map['description'] = Variable(description.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -255,6 +308,7 @@ class PlayerTableCompanion extends UpdateCompanion { return (StringBuffer('PlayerTableCompanion(') ..write('id: $id, ') ..write('name: $name, ') + ..write('description: $description, ') ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) @@ -286,6 +340,17 @@ class $GroupTableTable extends GroupTable type: DriftSqlType.string, requiredDuringInsert: true, ); + static const VerificationMeta _descriptionMeta = const VerificationMeta( + 'description', + ); + @override + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); static const VerificationMeta _createdAtMeta = const VerificationMeta( 'createdAt', ); @@ -298,7 +363,7 @@ class $GroupTableTable extends GroupTable requiredDuringInsert: true, ); @override - List get $columns => [id, name, createdAt]; + List get $columns => [id, name, description, createdAt]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -324,6 +389,17 @@ class $GroupTableTable extends GroupTable } else if (isInserting) { context.missing(_nameMeta); } + if (data.containsKey('description')) { + context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, + _descriptionMeta, + ), + ); + } else if (isInserting) { + context.missing(_descriptionMeta); + } if (data.containsKey('created_at')) { context.handle( _createdAtMeta, @@ -349,6 +425,10 @@ class $GroupTableTable extends GroupTable DriftSqlType.string, data['${effectivePrefix}name'], )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, createdAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}created_at'], @@ -365,10 +445,12 @@ class $GroupTableTable extends GroupTable class GroupTableData extends DataClass implements Insertable { final String id; final String name; + final String description; final DateTime createdAt; const GroupTableData({ required this.id, required this.name, + required this.description, required this.createdAt, }); @override @@ -376,6 +458,7 @@ class GroupTableData extends DataClass implements Insertable { final map = {}; map['id'] = Variable(id); map['name'] = Variable(name); + map['description'] = Variable(description); map['created_at'] = Variable(createdAt); return map; } @@ -384,6 +467,7 @@ class GroupTableData extends DataClass implements Insertable { return GroupTableCompanion( id: Value(id), name: Value(name), + description: Value(description), createdAt: Value(createdAt), ); } @@ -396,6 +480,7 @@ class GroupTableData extends DataClass implements Insertable { return GroupTableData( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), + description: serializer.fromJson(json['description']), createdAt: serializer.fromJson(json['createdAt']), ); } @@ -405,20 +490,29 @@ class GroupTableData extends DataClass implements Insertable { return { 'id': serializer.toJson(id), 'name': serializer.toJson(name), + 'description': serializer.toJson(description), 'createdAt': serializer.toJson(createdAt), }; } - GroupTableData copyWith({String? id, String? name, DateTime? createdAt}) => - GroupTableData( - id: id ?? this.id, - name: name ?? this.name, - createdAt: createdAt ?? this.createdAt, - ); + GroupTableData copyWith({ + String? id, + String? name, + String? description, + DateTime? createdAt, + }) => GroupTableData( + id: id ?? this.id, + name: name ?? this.name, + description: description ?? this.description, + createdAt: createdAt ?? this.createdAt, + ); GroupTableData copyWithCompanion(GroupTableCompanion data) { return GroupTableData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, + description: data.description.present + ? data.description.value + : this.description, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @@ -428,50 +522,58 @@ class GroupTableData extends DataClass implements Insertable { return (StringBuffer('GroupTableData(') ..write('id: $id, ') ..write('name: $name, ') + ..write('description: $description, ') ..write('createdAt: $createdAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, name, createdAt); + int get hashCode => Object.hash(id, name, description, createdAt); @override bool operator ==(Object other) => identical(this, other) || (other is GroupTableData && other.id == this.id && other.name == this.name && + other.description == this.description && other.createdAt == this.createdAt); } class GroupTableCompanion extends UpdateCompanion { final Value id; final Value name; + final Value description; final Value createdAt; final Value rowid; const GroupTableCompanion({ this.id = const Value.absent(), this.name = const Value.absent(), + this.description = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); GroupTableCompanion.insert({ required String id, required String name, + required String description, required DateTime createdAt, this.rowid = const Value.absent(), }) : id = Value(id), name = Value(name), + description = Value(description), createdAt = Value(createdAt); static Insertable custom({ Expression? id, Expression? name, + Expression? description, Expression? createdAt, Expression? rowid, }) { return RawValuesInsertable({ if (id != null) 'id': id, if (name != null) 'name': name, + if (description != null) 'description': description, if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); @@ -480,12 +582,14 @@ class GroupTableCompanion extends UpdateCompanion { GroupTableCompanion copyWith({ Value? id, Value? name, + Value? description, Value? createdAt, Value? rowid, }) { return GroupTableCompanion( id: id ?? this.id, name: name ?? this.name, + description: description ?? this.description, createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); @@ -500,6 +604,9 @@ class GroupTableCompanion extends UpdateCompanion { if (name.present) { map['name'] = Variable(name.value); } + if (description.present) { + map['description'] = Variable(description.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -514,6 +621,7 @@ class GroupTableCompanion extends UpdateCompanion { return (StringBuffer('GroupTableCompanion(') ..write('id: $id, ') ..write('name: $name, ') + ..write('description: $description, ') ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) @@ -521,23 +629,12 @@ class GroupTableCompanion extends UpdateCompanion { } } -class $MatchTableTable extends MatchTable - with TableInfo<$MatchTableTable, MatchTableData> { +class $GameTableTable extends GameTable + with TableInfo<$GameTableTable, GameTableData> { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $MatchTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _winnerIdMeta = const VerificationMeta( - 'winnerId', - ); - @override - late final GeneratedColumn winnerId = GeneratedColumn( - 'winner_id', - aliasedName, - true, - type: DriftSqlType.string, - requiredDuringInsert: false, - ); + $GameTableTable(this.attachedDatabase, [this._alias]); static const VerificationMeta _idMeta = const VerificationMeta('id'); @override late final GeneratedColumn id = GeneratedColumn( @@ -556,6 +653,46 @@ class $MatchTableTable extends MatchTable type: DriftSqlType.string, requiredDuringInsert: true, ); + static const VerificationMeta _rulesetMeta = const VerificationMeta( + 'ruleset', + ); + @override + late final GeneratedColumn ruleset = GeneratedColumn( + 'ruleset', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _descriptionMeta = const VerificationMeta( + 'description', + ); + @override + late final GeneratedColumn description = GeneratedColumn( + 'description', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _colorMeta = const VerificationMeta('color'); + @override + late final GeneratedColumn color = GeneratedColumn( + 'color', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _iconMeta = const VerificationMeta('icon'); + @override + late final GeneratedColumn icon = GeneratedColumn( + 'icon', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); static const VerificationMeta _createdAtMeta = const VerificationMeta( 'createdAt', ); @@ -568,25 +705,27 @@ class $MatchTableTable extends MatchTable requiredDuringInsert: true, ); @override - List get $columns => [winnerId, id, name, createdAt]; + List get $columns => [ + id, + name, + ruleset, + description, + color, + icon, + createdAt, + ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'match_table'; + static const String $name = 'game_table'; @override VerificationContext validateIntegrity( - Insertable instance, { + Insertable instance, { bool isInserting = false, }) { final context = VerificationContext(); final data = instance.toColumns(true); - if (data.containsKey('winner_id')) { - context.handle( - _winnerIdMeta, - winnerId.isAcceptableOrUnknown(data['winner_id']!, _winnerIdMeta), - ); - } if (data.containsKey('id')) { context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); } else if (isInserting) { @@ -600,6 +739,41 @@ class $MatchTableTable extends MatchTable } else if (isInserting) { context.missing(_nameMeta); } + if (data.containsKey('ruleset')) { + context.handle( + _rulesetMeta, + ruleset.isAcceptableOrUnknown(data['ruleset']!, _rulesetMeta), + ); + } else if (isInserting) { + context.missing(_rulesetMeta); + } + if (data.containsKey('description')) { + context.handle( + _descriptionMeta, + description.isAcceptableOrUnknown( + data['description']!, + _descriptionMeta, + ), + ); + } else if (isInserting) { + context.missing(_descriptionMeta); + } + if (data.containsKey('color')) { + context.handle( + _colorMeta, + color.isAcceptableOrUnknown(data['color']!, _colorMeta), + ); + } else if (isInserting) { + context.missing(_colorMeta); + } + if (data.containsKey('icon')) { + context.handle( + _iconMeta, + icon.isAcceptableOrUnknown(data['icon']!, _iconMeta), + ); + } else if (isInserting) { + context.missing(_iconMeta); + } if (data.containsKey('created_at')) { context.handle( _createdAtMeta, @@ -614,13 +788,9 @@ class $MatchTableTable extends MatchTable @override Set get $primaryKey => {id}; @override - MatchTableData map(Map data, {String? tablePrefix}) { + GameTableData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return MatchTableData( - winnerId: attachedDatabase.typeMapping.read( - DriftSqlType.string, - data['${effectivePrefix}winner_id'], - ), + return GameTableData( id: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}id'], @@ -629,6 +799,22 @@ class $MatchTableTable extends MatchTable DriftSqlType.string, data['${effectivePrefix}name'], )!, + ruleset: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}ruleset'], + )!, + description: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}description'], + )!, + color: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}color'], + )!, + icon: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}icon'], + )!, createdAt: attachedDatabase.typeMapping.read( DriftSqlType.dateTime, data['${effectivePrefix}created_at'], @@ -637,54 +823,65 @@ class $MatchTableTable extends MatchTable } @override - $MatchTableTable createAlias(String alias) { - return $MatchTableTable(attachedDatabase, alias); + $GameTableTable createAlias(String alias) { + return $GameTableTable(attachedDatabase, alias); } } -class MatchTableData extends DataClass implements Insertable { - final String? winnerId; +class GameTableData extends DataClass implements Insertable { final String id; final String name; + final String ruleset; + final String description; + final String color; + final String icon; final DateTime createdAt; - const MatchTableData({ - this.winnerId, + const GameTableData({ required this.id, required this.name, + required this.ruleset, + required this.description, + required this.color, + required this.icon, required this.createdAt, }); @override Map toColumns(bool nullToAbsent) { final map = {}; - if (!nullToAbsent || winnerId != null) { - map['winner_id'] = Variable(winnerId); - } map['id'] = Variable(id); map['name'] = Variable(name); + map['ruleset'] = Variable(ruleset); + map['description'] = Variable(description); + map['color'] = Variable(color); + map['icon'] = Variable(icon); map['created_at'] = Variable(createdAt); return map; } - MatchTableCompanion toCompanion(bool nullToAbsent) { - return MatchTableCompanion( - winnerId: winnerId == null && nullToAbsent - ? const Value.absent() - : Value(winnerId), + GameTableCompanion toCompanion(bool nullToAbsent) { + return GameTableCompanion( id: Value(id), name: Value(name), + ruleset: Value(ruleset), + description: Value(description), + color: Value(color), + icon: Value(icon), createdAt: Value(createdAt), ); } - factory MatchTableData.fromJson( + factory GameTableData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; - return MatchTableData( - winnerId: serializer.fromJson(json['winnerId']), + return GameTableData( id: serializer.fromJson(json['id']), name: serializer.fromJson(json['name']), + ruleset: serializer.fromJson(json['ruleset']), + description: serializer.fromJson(json['description']), + color: serializer.fromJson(json['color']), + icon: serializer.fromJson(json['icon']), createdAt: serializer.fromJson(json['createdAt']), ); } @@ -692,105 +889,151 @@ class MatchTableData extends DataClass implements Insertable { Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'winnerId': serializer.toJson(winnerId), 'id': serializer.toJson(id), 'name': serializer.toJson(name), + 'ruleset': serializer.toJson(ruleset), + 'description': serializer.toJson(description), + 'color': serializer.toJson(color), + 'icon': serializer.toJson(icon), 'createdAt': serializer.toJson(createdAt), }; } - MatchTableData copyWith({ - Value winnerId = const Value.absent(), + GameTableData copyWith({ String? id, String? name, + String? ruleset, + String? description, + String? color, + String? icon, DateTime? createdAt, - }) => MatchTableData( - winnerId: winnerId.present ? winnerId.value : this.winnerId, + }) => GameTableData( id: id ?? this.id, name: name ?? this.name, + ruleset: ruleset ?? this.ruleset, + description: description ?? this.description, + color: color ?? this.color, + icon: icon ?? this.icon, createdAt: createdAt ?? this.createdAt, ); - MatchTableData copyWithCompanion(MatchTableCompanion data) { - return MatchTableData( - winnerId: data.winnerId.present ? data.winnerId.value : this.winnerId, + GameTableData copyWithCompanion(GameTableCompanion data) { + return GameTableData( id: data.id.present ? data.id.value : this.id, name: data.name.present ? data.name.value : this.name, + ruleset: data.ruleset.present ? data.ruleset.value : this.ruleset, + description: data.description.present + ? data.description.value + : this.description, + color: data.color.present ? data.color.value : this.color, + icon: data.icon.present ? data.icon.value : this.icon, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, ); } @override String toString() { - return (StringBuffer('MatchTableData(') - ..write('winnerId: $winnerId, ') + return (StringBuffer('GameTableData(') ..write('id: $id, ') ..write('name: $name, ') + ..write('ruleset: $ruleset, ') + ..write('description: $description, ') + ..write('color: $color, ') + ..write('icon: $icon, ') ..write('createdAt: $createdAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(winnerId, id, name, createdAt); + int get hashCode => + Object.hash(id, name, ruleset, description, color, icon, createdAt); @override bool operator ==(Object other) => identical(this, other) || - (other is MatchTableData && - other.winnerId == this.winnerId && + (other is GameTableData && other.id == this.id && other.name == this.name && + other.ruleset == this.ruleset && + other.description == this.description && + other.color == this.color && + other.icon == this.icon && other.createdAt == this.createdAt); } -class MatchTableCompanion extends UpdateCompanion { - final Value winnerId; +class GameTableCompanion extends UpdateCompanion { final Value id; final Value name; + final Value ruleset; + final Value description; + final Value color; + final Value icon; final Value createdAt; final Value rowid; - const MatchTableCompanion({ - this.winnerId = const Value.absent(), + const GameTableCompanion({ this.id = const Value.absent(), this.name = const Value.absent(), + this.ruleset = const Value.absent(), + this.description = const Value.absent(), + this.color = const Value.absent(), + this.icon = const Value.absent(), this.createdAt = const Value.absent(), this.rowid = const Value.absent(), }); - MatchTableCompanion.insert({ - this.winnerId = const Value.absent(), + GameTableCompanion.insert({ required String id, required String name, + required String ruleset, + required String description, + required String color, + required String icon, required DateTime createdAt, this.rowid = const Value.absent(), }) : id = Value(id), name = Value(name), + ruleset = Value(ruleset), + description = Value(description), + color = Value(color), + icon = Value(icon), createdAt = Value(createdAt); - static Insertable custom({ - Expression? winnerId, + static Insertable custom({ Expression? id, Expression? name, + Expression? ruleset, + Expression? description, + Expression? color, + Expression? icon, Expression? createdAt, Expression? rowid, }) { return RawValuesInsertable({ - if (winnerId != null) 'winner_id': winnerId, if (id != null) 'id': id, if (name != null) 'name': name, + if (ruleset != null) 'ruleset': ruleset, + if (description != null) 'description': description, + if (color != null) 'color': color, + if (icon != null) 'icon': icon, if (createdAt != null) 'created_at': createdAt, if (rowid != null) 'rowid': rowid, }); } - MatchTableCompanion copyWith({ - Value? winnerId, + GameTableCompanion copyWith({ Value? id, Value? name, + Value? ruleset, + Value? description, + Value? color, + Value? icon, Value? createdAt, Value? rowid, }) { - return MatchTableCompanion( - winnerId: winnerId ?? this.winnerId, + return GameTableCompanion( id: id ?? this.id, name: name ?? this.name, + ruleset: ruleset ?? this.ruleset, + description: description ?? this.description, + color: color ?? this.color, + icon: icon ?? this.icon, createdAt: createdAt ?? this.createdAt, rowid: rowid ?? this.rowid, ); @@ -799,15 +1042,24 @@ class MatchTableCompanion extends UpdateCompanion { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (winnerId.present) { - map['winner_id'] = Variable(winnerId.value); - } if (id.present) { map['id'] = Variable(id.value); } if (name.present) { map['name'] = Variable(name.value); } + if (ruleset.present) { + map['ruleset'] = Variable(ruleset.value); + } + if (description.present) { + map['description'] = Variable(description.value); + } + if (color.present) { + map['color'] = Variable(color.value); + } + if (icon.present) { + map['icon'] = Variable(icon.value); + } if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } @@ -819,10 +1071,13 @@ class MatchTableCompanion extends UpdateCompanion { @override String toString() { - return (StringBuffer('MatchTableCompanion(') - ..write('winnerId: $winnerId, ') + return (StringBuffer('GameTableCompanion(') ..write('id: $id, ') ..write('name: $name, ') + ..write('ruleset: $ruleset, ') + ..write('description: $description, ') + ..write('color: $color, ') + ..write('icon: $icon, ') ..write('createdAt: $createdAt, ') ..write('rowid: $rowid') ..write(')')) @@ -830,6 +1085,465 @@ class MatchTableCompanion extends UpdateCompanion { } } +class $MatchTableTable extends MatchTable + with TableInfo<$MatchTableTable, MatchTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $MatchTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _gameIdMeta = const VerificationMeta('gameId'); + @override + late final GeneratedColumn gameId = GeneratedColumn( + 'game_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES game_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _groupIdMeta = const VerificationMeta( + 'groupId', + ); + @override + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES group_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _notesMeta = const VerificationMeta('notes'); + @override + late final GeneratedColumn notes = GeneratedColumn( + 'notes', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _createdAtMeta = const VerificationMeta( + 'createdAt', + ); + @override + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + static const VerificationMeta _endedAtMeta = const VerificationMeta( + 'endedAt', + ); + @override + late final GeneratedColumn endedAt = GeneratedColumn( + 'ended_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); + @override + List get $columns => [ + id, + gameId, + groupId, + name, + notes, + createdAt, + endedAt, + ]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'match_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('game_id')) { + context.handle( + _gameIdMeta, + gameId.isAcceptableOrUnknown(data['game_id']!, _gameIdMeta), + ); + } else if (isInserting) { + context.missing(_gameIdMeta); + } + if (data.containsKey('group_id')) { + context.handle( + _groupIdMeta, + groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), + ); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } + if (data.containsKey('notes')) { + context.handle( + _notesMeta, + notes.isAcceptableOrUnknown(data['notes']!, _notesMeta), + ); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + if (data.containsKey('ended_at')) { + context.handle( + _endedAtMeta, + endedAt.isAcceptableOrUnknown(data['ended_at']!, _endedAtMeta), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + MatchTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return MatchTableData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + gameId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}game_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + ), + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + ), + notes: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}notes'], + ), + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + endedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}ended_at'], + ), + ); + } + + @override + $MatchTableTable createAlias(String alias) { + return $MatchTableTable(attachedDatabase, alias); + } +} + +class MatchTableData extends DataClass implements Insertable { + final String id; + final String gameId; + final String? groupId; + final String? name; + final String? notes; + final DateTime createdAt; + final DateTime? endedAt; + const MatchTableData({ + required this.id, + required this.gameId, + this.groupId, + this.name, + this.notes, + required this.createdAt, + this.endedAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['game_id'] = Variable(gameId); + if (!nullToAbsent || groupId != null) { + map['group_id'] = Variable(groupId); + } + if (!nullToAbsent || name != null) { + map['name'] = Variable(name); + } + if (!nullToAbsent || notes != null) { + map['notes'] = Variable(notes); + } + map['created_at'] = Variable(createdAt); + if (!nullToAbsent || endedAt != null) { + map['ended_at'] = Variable(endedAt); + } + return map; + } + + MatchTableCompanion toCompanion(bool nullToAbsent) { + return MatchTableCompanion( + id: Value(id), + gameId: Value(gameId), + groupId: groupId == null && nullToAbsent + ? const Value.absent() + : Value(groupId), + name: name == null && nullToAbsent ? const Value.absent() : Value(name), + notes: notes == null && nullToAbsent + ? const Value.absent() + : Value(notes), + createdAt: Value(createdAt), + endedAt: endedAt == null && nullToAbsent + ? const Value.absent() + : Value(endedAt), + ); + } + + factory MatchTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return MatchTableData( + id: serializer.fromJson(json['id']), + gameId: serializer.fromJson(json['gameId']), + groupId: serializer.fromJson(json['groupId']), + name: serializer.fromJson(json['name']), + notes: serializer.fromJson(json['notes']), + createdAt: serializer.fromJson(json['createdAt']), + endedAt: serializer.fromJson(json['endedAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'gameId': serializer.toJson(gameId), + 'groupId': serializer.toJson(groupId), + 'name': serializer.toJson(name), + 'notes': serializer.toJson(notes), + 'createdAt': serializer.toJson(createdAt), + 'endedAt': serializer.toJson(endedAt), + }; + } + + MatchTableData copyWith({ + String? id, + String? gameId, + Value groupId = const Value.absent(), + Value name = const Value.absent(), + Value notes = const Value.absent(), + DateTime? createdAt, + Value endedAt = const Value.absent(), + }) => MatchTableData( + id: id ?? this.id, + gameId: gameId ?? this.gameId, + groupId: groupId.present ? groupId.value : this.groupId, + name: name.present ? name.value : this.name, + notes: notes.present ? notes.value : this.notes, + createdAt: createdAt ?? this.createdAt, + endedAt: endedAt.present ? endedAt.value : this.endedAt, + ); + MatchTableData copyWithCompanion(MatchTableCompanion data) { + return MatchTableData( + id: data.id.present ? data.id.value : this.id, + gameId: data.gameId.present ? data.gameId.value : this.gameId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + name: data.name.present ? data.name.value : this.name, + notes: data.notes.present ? data.notes.value : this.notes, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + endedAt: data.endedAt.present ? data.endedAt.value : this.endedAt, + ); + } + + @override + String toString() { + return (StringBuffer('MatchTableData(') + ..write('id: $id, ') + ..write('gameId: $gameId, ') + ..write('groupId: $groupId, ') + ..write('name: $name, ') + ..write('notes: $notes, ') + ..write('createdAt: $createdAt, ') + ..write('endedAt: $endedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + Object.hash(id, gameId, groupId, name, notes, createdAt, endedAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is MatchTableData && + other.id == this.id && + other.gameId == this.gameId && + other.groupId == this.groupId && + other.name == this.name && + other.notes == this.notes && + other.createdAt == this.createdAt && + other.endedAt == this.endedAt); +} + +class MatchTableCompanion extends UpdateCompanion { + final Value id; + final Value gameId; + final Value groupId; + final Value name; + final Value notes; + final Value createdAt; + final Value endedAt; + final Value rowid; + const MatchTableCompanion({ + this.id = const Value.absent(), + this.gameId = const Value.absent(), + this.groupId = const Value.absent(), + this.name = const Value.absent(), + this.notes = const Value.absent(), + this.createdAt = const Value.absent(), + this.endedAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + MatchTableCompanion.insert({ + required String id, + required String gameId, + this.groupId = const Value.absent(), + this.name = const Value.absent(), + this.notes = const Value.absent(), + required DateTime createdAt, + this.endedAt = const Value.absent(), + this.rowid = const Value.absent(), + }) : id = Value(id), + gameId = Value(gameId), + createdAt = Value(createdAt); + static Insertable custom({ + Expression? id, + Expression? gameId, + Expression? groupId, + Expression? name, + Expression? notes, + Expression? createdAt, + Expression? endedAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (gameId != null) 'game_id': gameId, + if (groupId != null) 'group_id': groupId, + if (name != null) 'name': name, + if (notes != null) 'notes': notes, + if (createdAt != null) 'created_at': createdAt, + if (endedAt != null) 'ended_at': endedAt, + if (rowid != null) 'rowid': rowid, + }); + } + + MatchTableCompanion copyWith({ + Value? id, + Value? gameId, + Value? groupId, + Value? name, + Value? notes, + Value? createdAt, + Value? endedAt, + Value? rowid, + }) { + return MatchTableCompanion( + id: id ?? this.id, + gameId: gameId ?? this.gameId, + groupId: groupId ?? this.groupId, + name: name ?? this.name, + notes: notes ?? this.notes, + createdAt: createdAt ?? this.createdAt, + endedAt: endedAt ?? this.endedAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (gameId.present) { + map['game_id'] = Variable(gameId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (notes.present) { + map['notes'] = Variable(notes.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (endedAt.present) { + map['ended_at'] = Variable(endedAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('MatchTableCompanion(') + ..write('id: $id, ') + ..write('gameId: $gameId, ') + ..write('groupId: $groupId, ') + ..write('name: $name, ') + ..write('notes: $notes, ') + ..write('createdAt: $createdAt, ') + ..write('endedAt: $endedAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + class $PlayerGroupTableTable extends PlayerGroupTable with TableInfo<$PlayerGroupTableTable, PlayerGroupTableData> { @override @@ -1055,6 +1769,265 @@ class PlayerGroupTableCompanion extends UpdateCompanion { } } +class $TeamTableTable extends TeamTable + with TableInfo<$TeamTableTable, TeamTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $TeamTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _idMeta = const VerificationMeta('id'); + @override + late final GeneratedColumn id = GeneratedColumn( + 'id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _nameMeta = const VerificationMeta('name'); + @override + late final GeneratedColumn name = GeneratedColumn( + 'name', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _createdAtMeta = const VerificationMeta( + 'createdAt', + ); + @override + late final GeneratedColumn createdAt = GeneratedColumn( + 'created_at', + aliasedName, + false, + type: DriftSqlType.dateTime, + requiredDuringInsert: true, + ); + @override + List get $columns => [id, name, createdAt]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'team_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('id')) { + context.handle(_idMeta, id.isAcceptableOrUnknown(data['id']!, _idMeta)); + } else if (isInserting) { + context.missing(_idMeta); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, + name.isAcceptableOrUnknown(data['name']!, _nameMeta), + ); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('created_at')) { + context.handle( + _createdAtMeta, + createdAt.isAcceptableOrUnknown(data['created_at']!, _createdAtMeta), + ); + } else if (isInserting) { + context.missing(_createdAtMeta); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + TeamTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return TeamTableData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + name: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}name'], + )!, + createdAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}created_at'], + )!, + ); + } + + @override + $TeamTableTable createAlias(String alias) { + return $TeamTableTable(attachedDatabase, alias); + } +} + +class TeamTableData extends DataClass implements Insertable { + final String id; + final String name; + final DateTime createdAt; + const TeamTableData({ + required this.id, + required this.name, + required this.createdAt, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['name'] = Variable(name); + map['created_at'] = Variable(createdAt); + return map; + } + + TeamTableCompanion toCompanion(bool nullToAbsent) { + return TeamTableCompanion( + id: Value(id), + name: Value(name), + createdAt: Value(createdAt), + ); + } + + factory TeamTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return TeamTableData( + id: serializer.fromJson(json['id']), + name: serializer.fromJson(json['name']), + createdAt: serializer.fromJson(json['createdAt']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'name': serializer.toJson(name), + 'createdAt': serializer.toJson(createdAt), + }; + } + + TeamTableData copyWith({String? id, String? name, DateTime? createdAt}) => + TeamTableData( + id: id ?? this.id, + name: name ?? this.name, + createdAt: createdAt ?? this.createdAt, + ); + TeamTableData copyWithCompanion(TeamTableCompanion data) { + return TeamTableData( + id: data.id.present ? data.id.value : this.id, + name: data.name.present ? data.name.value : this.name, + createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + ); + } + + @override + String toString() { + return (StringBuffer('TeamTableData(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('createdAt: $createdAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, name, createdAt); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is TeamTableData && + other.id == this.id && + other.name == this.name && + other.createdAt == this.createdAt); +} + +class TeamTableCompanion extends UpdateCompanion { + final Value id; + final Value name; + final Value createdAt; + final Value rowid; + const TeamTableCompanion({ + this.id = const Value.absent(), + this.name = const Value.absent(), + this.createdAt = const Value.absent(), + this.rowid = const Value.absent(), + }); + TeamTableCompanion.insert({ + required String id, + required String name, + required DateTime createdAt, + this.rowid = const Value.absent(), + }) : id = Value(id), + name = Value(name), + createdAt = Value(createdAt); + static Insertable custom({ + Expression? id, + Expression? name, + Expression? createdAt, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (name != null) 'name': name, + if (createdAt != null) 'created_at': createdAt, + if (rowid != null) 'rowid': rowid, + }); + } + + TeamTableCompanion copyWith({ + Value? id, + Value? name, + Value? createdAt, + Value? rowid, + }) { + return TeamTableCompanion( + id: id ?? this.id, + name: name ?? this.name, + createdAt: createdAt ?? this.createdAt, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (createdAt.present) { + map['created_at'] = Variable(createdAt.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('TeamTableCompanion(') + ..write('id: $id, ') + ..write('name: $name, ') + ..write('createdAt: $createdAt, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + class $PlayerMatchTableTable extends PlayerMatchTable with TableInfo<$PlayerMatchTableTable, PlayerMatchTableData> { @override @@ -1089,8 +2062,29 @@ class $PlayerMatchTableTable extends PlayerMatchTable 'REFERENCES match_table (id) ON DELETE CASCADE', ), ); + static const VerificationMeta _teamIdMeta = const VerificationMeta('teamId'); @override - List get $columns => [playerId, matchId]; + late final GeneratedColumn teamId = GeneratedColumn( + 'team_id', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES team_table (id)', + ), + ); + static const VerificationMeta _scoreMeta = const VerificationMeta('score'); + @override + late final GeneratedColumn score = GeneratedColumn( + 'score', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [playerId, matchId, teamId, score]; @override String get aliasedName => _alias ?? actualTableName; @override @@ -1119,6 +2113,20 @@ class $PlayerMatchTableTable extends PlayerMatchTable } else if (isInserting) { context.missing(_matchIdMeta); } + if (data.containsKey('team_id')) { + context.handle( + _teamIdMeta, + teamId.isAcceptableOrUnknown(data['team_id']!, _teamIdMeta), + ); + } + if (data.containsKey('score')) { + context.handle( + _scoreMeta, + score.isAcceptableOrUnknown(data['score']!, _scoreMeta), + ); + } else if (isInserting) { + context.missing(_scoreMeta); + } return context; } @@ -1136,6 +2144,14 @@ class $PlayerMatchTableTable extends PlayerMatchTable DriftSqlType.string, data['${effectivePrefix}match_id'], )!, + teamId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}team_id'], + ), + score: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}score'], + )!, ); } @@ -1149,12 +2165,23 @@ class PlayerMatchTableData extends DataClass implements Insertable { final String playerId; final String matchId; - const PlayerMatchTableData({required this.playerId, required this.matchId}); + final String? teamId; + final int score; + const PlayerMatchTableData({ + required this.playerId, + required this.matchId, + this.teamId, + required this.score, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; map['player_id'] = Variable(playerId); map['match_id'] = Variable(matchId); + if (!nullToAbsent || teamId != null) { + map['team_id'] = Variable(teamId); + } + map['score'] = Variable(score); return map; } @@ -1162,6 +2189,10 @@ class PlayerMatchTableData extends DataClass return PlayerMatchTableCompanion( playerId: Value(playerId), matchId: Value(matchId), + teamId: teamId == null && nullToAbsent + ? const Value.absent() + : Value(teamId), + score: Value(score), ); } @@ -1173,6 +2204,8 @@ class PlayerMatchTableData extends DataClass return PlayerMatchTableData( playerId: serializer.fromJson(json['playerId']), matchId: serializer.fromJson(json['matchId']), + teamId: serializer.fromJson(json['teamId']), + score: serializer.fromJson(json['score']), ); } @override @@ -1181,18 +2214,28 @@ class PlayerMatchTableData extends DataClass return { 'playerId': serializer.toJson(playerId), 'matchId': serializer.toJson(matchId), + 'teamId': serializer.toJson(teamId), + 'score': serializer.toJson(score), }; } - PlayerMatchTableData copyWith({String? playerId, String? matchId}) => - PlayerMatchTableData( - playerId: playerId ?? this.playerId, - matchId: matchId ?? this.matchId, - ); + PlayerMatchTableData copyWith({ + String? playerId, + String? matchId, + Value teamId = const Value.absent(), + int? score, + }) => PlayerMatchTableData( + playerId: playerId ?? this.playerId, + matchId: matchId ?? this.matchId, + teamId: teamId.present ? teamId.value : this.teamId, + score: score ?? this.score, + ); PlayerMatchTableData copyWithCompanion(PlayerMatchTableCompanion data) { return PlayerMatchTableData( playerId: data.playerId.present ? data.playerId.value : this.playerId, matchId: data.matchId.present ? data.matchId.value : this.matchId, + teamId: data.teamId.present ? data.teamId.value : this.teamId, + score: data.score.present ? data.score.value : this.score, ); } @@ -1200,44 +2243,59 @@ class PlayerMatchTableData extends DataClass String toString() { return (StringBuffer('PlayerMatchTableData(') ..write('playerId: $playerId, ') - ..write('matchId: $matchId') + ..write('matchId: $matchId, ') + ..write('teamId: $teamId, ') + ..write('score: $score') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(playerId, matchId); + int get hashCode => Object.hash(playerId, matchId, teamId, score); @override bool operator ==(Object other) => identical(this, other) || (other is PlayerMatchTableData && other.playerId == this.playerId && - other.matchId == this.matchId); + other.matchId == this.matchId && + other.teamId == this.teamId && + other.score == this.score); } class PlayerMatchTableCompanion extends UpdateCompanion { final Value playerId; final Value matchId; + final Value teamId; + final Value score; final Value rowid; const PlayerMatchTableCompanion({ this.playerId = const Value.absent(), this.matchId = const Value.absent(), + this.teamId = const Value.absent(), + this.score = const Value.absent(), this.rowid = const Value.absent(), }); PlayerMatchTableCompanion.insert({ required String playerId, required String matchId, + this.teamId = const Value.absent(), + required int score, this.rowid = const Value.absent(), }) : playerId = Value(playerId), - matchId = Value(matchId); + matchId = Value(matchId), + score = Value(score); static Insertable custom({ Expression? playerId, Expression? matchId, + Expression? teamId, + Expression? score, Expression? rowid, }) { return RawValuesInsertable({ if (playerId != null) 'player_id': playerId, if (matchId != null) 'match_id': matchId, + if (teamId != null) 'team_id': teamId, + if (score != null) 'score': score, if (rowid != null) 'rowid': rowid, }); } @@ -1245,11 +2303,15 @@ class PlayerMatchTableCompanion extends UpdateCompanion { PlayerMatchTableCompanion copyWith({ Value? playerId, Value? matchId, + Value? teamId, + Value? score, Value? rowid, }) { return PlayerMatchTableCompanion( playerId: playerId ?? this.playerId, matchId: matchId ?? this.matchId, + teamId: teamId ?? this.teamId, + score: score ?? this.score, rowid: rowid ?? this.rowid, ); } @@ -1263,6 +2325,12 @@ class PlayerMatchTableCompanion extends UpdateCompanion { if (matchId.present) { map['match_id'] = Variable(matchId.value); } + if (teamId.present) { + map['team_id'] = Variable(teamId.value); + } + if (score.present) { + map['score'] = Variable(score.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -1274,30 +2342,32 @@ class PlayerMatchTableCompanion extends UpdateCompanion { return (StringBuffer('PlayerMatchTableCompanion(') ..write('playerId: $playerId, ') ..write('matchId: $matchId, ') + ..write('teamId: $teamId, ') + ..write('score: $score, ') ..write('rowid: $rowid') ..write(')')) .toString(); } } -class $GroupMatchTableTable extends GroupMatchTable - with TableInfo<$GroupMatchTableTable, GroupMatchTableData> { +class $ScoreTableTable extends ScoreTable + with TableInfo<$ScoreTableTable, ScoreTableData> { @override final GeneratedDatabase attachedDatabase; final String? _alias; - $GroupMatchTableTable(this.attachedDatabase, [this._alias]); - static const VerificationMeta _groupIdMeta = const VerificationMeta( - 'groupId', + $ScoreTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _playerIdMeta = const VerificationMeta( + 'playerId', ); @override - late final GeneratedColumn groupId = GeneratedColumn( - 'group_id', + late final GeneratedColumn playerId = GeneratedColumn( + 'player_id', aliasedName, false, type: DriftSqlType.string, requiredDuringInsert: true, defaultConstraints: GeneratedColumn.constraintIsAlways( - 'REFERENCES group_table (id) ON DELETE CASCADE', + 'REFERENCES player_table (id) ON DELETE CASCADE', ), ); static const VerificationMeta _matchIdMeta = const VerificationMeta( @@ -1314,27 +2384,62 @@ class $GroupMatchTableTable extends GroupMatchTable 'REFERENCES match_table (id) ON DELETE CASCADE', ), ); + static const VerificationMeta _roundNumberMeta = const VerificationMeta( + 'roundNumber', + ); @override - List get $columns => [groupId, matchId]; + late final GeneratedColumn roundNumber = GeneratedColumn( + 'round_number', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _scoreMeta = const VerificationMeta('score'); + @override + late final GeneratedColumn score = GeneratedColumn( + 'score', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + static const VerificationMeta _changeMeta = const VerificationMeta('change'); + @override + late final GeneratedColumn change = GeneratedColumn( + 'change', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: true, + ); + @override + List get $columns => [ + playerId, + matchId, + roundNumber, + score, + change, + ]; @override String get aliasedName => _alias ?? actualTableName; @override String get actualTableName => $name; - static const String $name = 'group_match_table'; + static const String $name = 'score_table'; @override VerificationContext validateIntegrity( - Insertable instance, { + Insertable instance, { bool isInserting = false, }) { final context = VerificationContext(); final data = instance.toColumns(true); - if (data.containsKey('group_id')) { + if (data.containsKey('player_id')) { context.handle( - _groupIdMeta, - groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), + _playerIdMeta, + playerId.isAcceptableOrUnknown(data['player_id']!, _playerIdMeta), ); } else if (isInserting) { - context.missing(_groupIdMeta); + context.missing(_playerIdMeta); } if (data.containsKey('match_id')) { context.handle( @@ -1344,137 +2449,240 @@ class $GroupMatchTableTable extends GroupMatchTable } else if (isInserting) { context.missing(_matchIdMeta); } + if (data.containsKey('round_number')) { + context.handle( + _roundNumberMeta, + roundNumber.isAcceptableOrUnknown( + data['round_number']!, + _roundNumberMeta, + ), + ); + } else if (isInserting) { + context.missing(_roundNumberMeta); + } + if (data.containsKey('score')) { + context.handle( + _scoreMeta, + score.isAcceptableOrUnknown(data['score']!, _scoreMeta), + ); + } else if (isInserting) { + context.missing(_scoreMeta); + } + if (data.containsKey('change')) { + context.handle( + _changeMeta, + change.isAcceptableOrUnknown(data['change']!, _changeMeta), + ); + } else if (isInserting) { + context.missing(_changeMeta); + } return context; } @override - Set get $primaryKey => {groupId, matchId}; + Set get $primaryKey => {playerId, matchId, roundNumber}; @override - GroupMatchTableData map(Map data, {String? tablePrefix}) { + ScoreTableData map(Map data, {String? tablePrefix}) { final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; - return GroupMatchTableData( - groupId: attachedDatabase.typeMapping.read( + return ScoreTableData( + playerId: attachedDatabase.typeMapping.read( DriftSqlType.string, - data['${effectivePrefix}group_id'], + data['${effectivePrefix}player_id'], )!, matchId: attachedDatabase.typeMapping.read( DriftSqlType.string, data['${effectivePrefix}match_id'], )!, + roundNumber: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}round_number'], + )!, + score: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}score'], + )!, + change: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}change'], + )!, ); } @override - $GroupMatchTableTable createAlias(String alias) { - return $GroupMatchTableTable(attachedDatabase, alias); + $ScoreTableTable createAlias(String alias) { + return $ScoreTableTable(attachedDatabase, alias); } } -class GroupMatchTableData extends DataClass - implements Insertable { - final String groupId; +class ScoreTableData extends DataClass implements Insertable { + final String playerId; final String matchId; - const GroupMatchTableData({required this.groupId, required this.matchId}); + final int roundNumber; + final int score; + final int change; + const ScoreTableData({ + required this.playerId, + required this.matchId, + required this.roundNumber, + required this.score, + required this.change, + }); @override Map toColumns(bool nullToAbsent) { final map = {}; - map['group_id'] = Variable(groupId); + map['player_id'] = Variable(playerId); map['match_id'] = Variable(matchId); + map['round_number'] = Variable(roundNumber); + map['score'] = Variable(score); + map['change'] = Variable(change); return map; } - GroupMatchTableCompanion toCompanion(bool nullToAbsent) { - return GroupMatchTableCompanion( - groupId: Value(groupId), + ScoreTableCompanion toCompanion(bool nullToAbsent) { + return ScoreTableCompanion( + playerId: Value(playerId), matchId: Value(matchId), + roundNumber: Value(roundNumber), + score: Value(score), + change: Value(change), ); } - factory GroupMatchTableData.fromJson( + factory ScoreTableData.fromJson( Map json, { ValueSerializer? serializer, }) { serializer ??= driftRuntimeOptions.defaultSerializer; - return GroupMatchTableData( - groupId: serializer.fromJson(json['groupId']), + return ScoreTableData( + playerId: serializer.fromJson(json['playerId']), matchId: serializer.fromJson(json['matchId']), + roundNumber: serializer.fromJson(json['roundNumber']), + score: serializer.fromJson(json['score']), + change: serializer.fromJson(json['change']), ); } @override Map toJson({ValueSerializer? serializer}) { serializer ??= driftRuntimeOptions.defaultSerializer; return { - 'groupId': serializer.toJson(groupId), + 'playerId': serializer.toJson(playerId), 'matchId': serializer.toJson(matchId), + 'roundNumber': serializer.toJson(roundNumber), + 'score': serializer.toJson(score), + 'change': serializer.toJson(change), }; } - GroupMatchTableData copyWith({String? groupId, String? matchId}) => - GroupMatchTableData( - groupId: groupId ?? this.groupId, - matchId: matchId ?? this.matchId, - ); - GroupMatchTableData copyWithCompanion(GroupMatchTableCompanion data) { - return GroupMatchTableData( - groupId: data.groupId.present ? data.groupId.value : this.groupId, + ScoreTableData copyWith({ + String? playerId, + String? matchId, + int? roundNumber, + int? score, + int? change, + }) => ScoreTableData( + playerId: playerId ?? this.playerId, + matchId: matchId ?? this.matchId, + roundNumber: roundNumber ?? this.roundNumber, + score: score ?? this.score, + change: change ?? this.change, + ); + ScoreTableData copyWithCompanion(ScoreTableCompanion data) { + return ScoreTableData( + playerId: data.playerId.present ? data.playerId.value : this.playerId, matchId: data.matchId.present ? data.matchId.value : this.matchId, + roundNumber: data.roundNumber.present + ? data.roundNumber.value + : this.roundNumber, + score: data.score.present ? data.score.value : this.score, + change: data.change.present ? data.change.value : this.change, ); } @override String toString() { - return (StringBuffer('GroupMatchTableData(') - ..write('groupId: $groupId, ') - ..write('matchId: $matchId') + return (StringBuffer('ScoreTableData(') + ..write('playerId: $playerId, ') + ..write('matchId: $matchId, ') + ..write('roundNumber: $roundNumber, ') + ..write('score: $score, ') + ..write('change: $change') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(groupId, matchId); + int get hashCode => + Object.hash(playerId, matchId, roundNumber, score, change); @override bool operator ==(Object other) => identical(this, other) || - (other is GroupMatchTableData && - other.groupId == this.groupId && - other.matchId == this.matchId); + (other is ScoreTableData && + other.playerId == this.playerId && + other.matchId == this.matchId && + other.roundNumber == this.roundNumber && + other.score == this.score && + other.change == this.change); } -class GroupMatchTableCompanion extends UpdateCompanion { - final Value groupId; +class ScoreTableCompanion extends UpdateCompanion { + final Value playerId; final Value matchId; + final Value roundNumber; + final Value score; + final Value change; final Value rowid; - const GroupMatchTableCompanion({ - this.groupId = const Value.absent(), + const ScoreTableCompanion({ + this.playerId = const Value.absent(), this.matchId = const Value.absent(), + this.roundNumber = const Value.absent(), + this.score = const Value.absent(), + this.change = const Value.absent(), this.rowid = const Value.absent(), }); - GroupMatchTableCompanion.insert({ - required String groupId, + ScoreTableCompanion.insert({ + required String playerId, required String matchId, + required int roundNumber, + required int score, + required int change, this.rowid = const Value.absent(), - }) : groupId = Value(groupId), - matchId = Value(matchId); - static Insertable custom({ - Expression? groupId, + }) : playerId = Value(playerId), + matchId = Value(matchId), + roundNumber = Value(roundNumber), + score = Value(score), + change = Value(change); + static Insertable custom({ + Expression? playerId, Expression? matchId, + Expression? roundNumber, + Expression? score, + Expression? change, Expression? rowid, }) { return RawValuesInsertable({ - if (groupId != null) 'group_id': groupId, + if (playerId != null) 'player_id': playerId, if (matchId != null) 'match_id': matchId, + if (roundNumber != null) 'round_number': roundNumber, + if (score != null) 'score': score, + if (change != null) 'change': change, if (rowid != null) 'rowid': rowid, }); } - GroupMatchTableCompanion copyWith({ - Value? groupId, + ScoreTableCompanion copyWith({ + Value? playerId, Value? matchId, + Value? roundNumber, + Value? score, + Value? change, Value? rowid, }) { - return GroupMatchTableCompanion( - groupId: groupId ?? this.groupId, + return ScoreTableCompanion( + playerId: playerId ?? this.playerId, matchId: matchId ?? this.matchId, + roundNumber: roundNumber ?? this.roundNumber, + score: score ?? this.score, + change: change ?? this.change, rowid: rowid ?? this.rowid, ); } @@ -1482,12 +2690,21 @@ class GroupMatchTableCompanion extends UpdateCompanion { @override Map toColumns(bool nullToAbsent) { final map = {}; - if (groupId.present) { - map['group_id'] = Variable(groupId.value); + if (playerId.present) { + map['player_id'] = Variable(playerId.value); } if (matchId.present) { map['match_id'] = Variable(matchId.value); } + if (roundNumber.present) { + map['round_number'] = Variable(roundNumber.value); + } + if (score.present) { + map['score'] = Variable(score.value); + } + if (change.present) { + map['change'] = Variable(change.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -1496,9 +2713,12 @@ class GroupMatchTableCompanion extends UpdateCompanion { @override String toString() { - return (StringBuffer('GroupMatchTableCompanion(') - ..write('groupId: $groupId, ') + return (StringBuffer('ScoreTableCompanion(') + ..write('playerId: $playerId, ') ..write('matchId: $matchId, ') + ..write('roundNumber: $roundNumber, ') + ..write('score: $score, ') + ..write('change: $change, ') ..write('rowid: $rowid') ..write(')')) .toString(); @@ -1510,16 +2730,16 @@ abstract class _$AppDatabase extends GeneratedDatabase { $AppDatabaseManager get managers => $AppDatabaseManager(this); late final $PlayerTableTable playerTable = $PlayerTableTable(this); late final $GroupTableTable groupTable = $GroupTableTable(this); + late final $GameTableTable gameTable = $GameTableTable(this); late final $MatchTableTable matchTable = $MatchTableTable(this); late final $PlayerGroupTableTable playerGroupTable = $PlayerGroupTableTable( this, ); + late final $TeamTableTable teamTable = $TeamTableTable(this); late final $PlayerMatchTableTable playerMatchTable = $PlayerMatchTableTable( this, ); - late final $GroupMatchTableTable groupMatchTable = $GroupMatchTableTable( - this, - ); + late final $ScoreTableTable scoreTable = $ScoreTableTable(this); late final PlayerDao playerDao = PlayerDao(this as AppDatabase); late final GroupDao groupDao = GroupDao(this as AppDatabase); late final MatchDao matchDao = MatchDao(this as AppDatabase); @@ -1529,7 +2749,9 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final PlayerMatchDao playerMatchDao = PlayerMatchDao( this as AppDatabase, ); - late final GroupMatchDao groupMatchDao = GroupMatchDao(this as AppDatabase); + late final GameDao gameDao = GameDao(this as AppDatabase); + late final ScoreDao scoreDao = ScoreDao(this as AppDatabase); + late final TeamDao teamDao = TeamDao(this as AppDatabase); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -1537,13 +2759,29 @@ abstract class _$AppDatabase extends GeneratedDatabase { List get allSchemaEntities => [ playerTable, groupTable, + gameTable, matchTable, playerGroupTable, + teamTable, playerMatchTable, - groupMatchTable, + scoreTable, ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ + WritePropagation( + on: TableUpdateQuery.onTableName( + 'game_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('match_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'group_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('match_table', kind: UpdateKind.delete)], + ), WritePropagation( on: TableUpdateQuery.onTableName( 'player_table', @@ -1574,17 +2812,17 @@ abstract class _$AppDatabase extends GeneratedDatabase { ), WritePropagation( on: TableUpdateQuery.onTableName( - 'group_table', + 'player_table', limitUpdateKind: UpdateKind.delete, ), - result: [TableUpdate('group_match_table', kind: UpdateKind.delete)], + result: [TableUpdate('score_table', kind: UpdateKind.delete)], ), WritePropagation( on: TableUpdateQuery.onTableName( 'match_table', limitUpdateKind: UpdateKind.delete, ), - result: [TableUpdate('group_match_table', kind: UpdateKind.delete)], + result: [TableUpdate('score_table', kind: UpdateKind.delete)], ), ]); } @@ -1593,6 +2831,7 @@ typedef $$PlayerTableTableCreateCompanionBuilder = PlayerTableCompanion Function({ required String id, required String name, + required String description, required DateTime createdAt, Value rowid, }); @@ -1600,6 +2839,7 @@ typedef $$PlayerTableTableUpdateCompanionBuilder = PlayerTableCompanion Function({ Value id, Value name, + Value description, Value createdAt, Value rowid, }); @@ -1653,6 +2893,24 @@ final class $$PlayerTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey<$ScoreTableTable, List> + _scoreTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.scoreTable, + aliasName: $_aliasNameGenerator(db.playerTable.id, db.scoreTable.playerId), + ); + + $$ScoreTableTableProcessedTableManager get scoreTableRefs { + final manager = $$ScoreTableTableTableManager( + $_db, + $_db.scoreTable, + ).filter((f) => f.playerId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_scoreTableRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$PlayerTableTableFilterComposer @@ -1674,6 +2932,11 @@ class $$PlayerTableTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column), @@ -1728,6 +2991,31 @@ class $$PlayerTableTableFilterComposer ); return f(composer); } + + Expression scoreTableRefs( + Expression Function($$ScoreTableTableFilterComposer f) f, + ) { + final $$ScoreTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.scoreTable, + getReferencedColumn: (t) => t.playerId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ScoreTableTableFilterComposer( + $db: $db, + $table: $db.scoreTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$PlayerTableTableOrderingComposer @@ -1749,6 +3037,11 @@ class $$PlayerTableTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column), @@ -1770,6 +3063,11 @@ class $$PlayerTableTableAnnotationComposer GeneratedColumn get name => $composableBuilder(column: $table.name, builder: (column) => column); + GeneratedColumn get description => $composableBuilder( + column: $table.description, + builder: (column) => column, + ); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); @@ -1822,6 +3120,31 @@ class $$PlayerTableTableAnnotationComposer ); return f(composer); } + + Expression scoreTableRefs( + Expression Function($$ScoreTableTableAnnotationComposer a) f, + ) { + final $$ScoreTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.scoreTable, + getReferencedColumn: (t) => t.playerId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$ScoreTableTableAnnotationComposer( + $db: $db, + $table: $db.scoreTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$PlayerTableTableTableManager @@ -1840,6 +3163,7 @@ class $$PlayerTableTableTableManager PrefetchHooks Function({ bool playerGroupTableRefs, bool playerMatchTableRefs, + bool scoreTableRefs, }) > { $$PlayerTableTableTableManager(_$AppDatabase db, $PlayerTableTable table) @@ -1857,11 +3181,13 @@ class $$PlayerTableTableTableManager ({ Value id = const Value.absent(), Value name = const Value.absent(), + Value description = const Value.absent(), Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => PlayerTableCompanion( id: id, name: name, + description: description, createdAt: createdAt, rowid: rowid, ), @@ -1869,11 +3195,13 @@ class $$PlayerTableTableTableManager ({ required String id, required String name, + required String description, required DateTime createdAt, Value rowid = const Value.absent(), }) => PlayerTableCompanion.insert( id: id, name: name, + description: description, createdAt: createdAt, rowid: rowid, ), @@ -1886,12 +3214,17 @@ class $$PlayerTableTableTableManager ) .toList(), prefetchHooksCallback: - ({playerGroupTableRefs = false, playerMatchTableRefs = false}) { + ({ + playerGroupTableRefs = false, + playerMatchTableRefs = false, + scoreTableRefs = false, + }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (playerGroupTableRefs) db.playerGroupTable, if (playerMatchTableRefs) db.playerMatchTable, + if (scoreTableRefs) db.scoreTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -1938,6 +3271,27 @@ class $$PlayerTableTableTableManager ), typedResults: items, ), + if (scoreTableRefs) + await $_getPrefetchedData< + PlayerTableData, + $PlayerTableTable, + ScoreTableData + >( + currentTable: table, + referencedTable: $$PlayerTableTableReferences + ._scoreTableRefsTable(db), + managerFromTypedResult: (p0) => + $$PlayerTableTableReferences( + db, + table, + p0, + ).scoreTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.playerId == item.id, + ), + typedResults: items, + ), ]; }, ); @@ -1961,12 +3315,14 @@ typedef $$PlayerTableTableProcessedTableManager = PrefetchHooks Function({ bool playerGroupTableRefs, bool playerMatchTableRefs, + bool scoreTableRefs, }) >; typedef $$GroupTableTableCreateCompanionBuilder = GroupTableCompanion Function({ required String id, required String name, + required String description, required DateTime createdAt, Value rowid, }); @@ -1974,6 +3330,7 @@ typedef $$GroupTableTableUpdateCompanionBuilder = GroupTableCompanion Function({ Value id, Value name, + Value description, Value createdAt, Value rowid, }); @@ -1982,6 +3339,24 @@ final class $$GroupTableTableReferences extends BaseReferences<_$AppDatabase, $GroupTableTable, GroupTableData> { $$GroupTableTableReferences(super.$_db, super.$_table, super.$_typedResult); + static MultiTypedResultKey<$MatchTableTable, List> + _matchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.matchTable, + aliasName: $_aliasNameGenerator(db.groupTable.id, db.matchTable.groupId), + ); + + $$MatchTableTableProcessedTableManager get matchTableRefs { + final manager = $$MatchTableTableTableManager( + $_db, + $_db.matchTable, + ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_matchTableRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + static MultiTypedResultKey<$PlayerGroupTableTable, List> _playerGroupTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( db.playerGroupTable, @@ -2004,29 +3379,6 @@ final class $$GroupTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } - - static MultiTypedResultKey<$GroupMatchTableTable, List> - _groupMatchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( - db.groupMatchTable, - aliasName: $_aliasNameGenerator( - db.groupTable.id, - db.groupMatchTable.groupId, - ), - ); - - $$GroupMatchTableTableProcessedTableManager get groupMatchTableRefs { - final manager = $$GroupMatchTableTableTableManager( - $_db, - $_db.groupMatchTable, - ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); - - final cache = $_typedResult.readTableOrNull( - _groupMatchTableRefsTable($_db), - ); - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: cache), - ); - } } class $$GroupTableTableFilterComposer @@ -2048,11 +3400,41 @@ class $$GroupTableTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column), ); + Expression matchTableRefs( + Expression Function($$MatchTableTableFilterComposer f) f, + ) { + final $$MatchTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableFilterComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + Expression playerGroupTableRefs( Expression Function($$PlayerGroupTableTableFilterComposer f) f, ) { @@ -2077,31 +3459,6 @@ class $$GroupTableTableFilterComposer ); return f(composer); } - - Expression groupMatchTableRefs( - Expression Function($$GroupMatchTableTableFilterComposer f) f, - ) { - final $$GroupMatchTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.groupMatchTable, - getReferencedColumn: (t) => t.groupId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupMatchTableTableFilterComposer( - $db: $db, - $table: $db.groupMatchTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GroupTableTableOrderingComposer @@ -2123,6 +3480,11 @@ class $$GroupTableTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column), @@ -2144,9 +3506,39 @@ class $$GroupTableTableAnnotationComposer GeneratedColumn get name => $composableBuilder(column: $table.name, builder: (column) => column); + GeneratedColumn get description => $composableBuilder( + column: $table.description, + builder: (column) => column, + ); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); + Expression matchTableRefs( + Expression Function($$MatchTableTableAnnotationComposer a) f, + ) { + final $$MatchTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableAnnotationComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + Expression playerGroupTableRefs( Expression Function($$PlayerGroupTableTableAnnotationComposer a) f, ) { @@ -2171,31 +3563,6 @@ class $$GroupTableTableAnnotationComposer ); return f(composer); } - - Expression groupMatchTableRefs( - Expression Function($$GroupMatchTableTableAnnotationComposer a) f, - ) { - final $$GroupMatchTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.id, - referencedTable: $db.groupMatchTable, - getReferencedColumn: (t) => t.groupId, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupMatchTableTableAnnotationComposer( - $db: $db, - $table: $db.groupMatchTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return f(composer); - } } class $$GroupTableTableTableManager @@ -2212,8 +3579,8 @@ class $$GroupTableTableTableManager (GroupTableData, $$GroupTableTableReferences), GroupTableData, PrefetchHooks Function({ + bool matchTableRefs, bool playerGroupTableRefs, - bool groupMatchTableRefs, }) > { $$GroupTableTableTableManager(_$AppDatabase db, $GroupTableTable table) @@ -2231,11 +3598,13 @@ class $$GroupTableTableTableManager ({ Value id = const Value.absent(), Value name = const Value.absent(), + Value description = const Value.absent(), Value createdAt = const Value.absent(), Value rowid = const Value.absent(), }) => GroupTableCompanion( id: id, name: name, + description: description, createdAt: createdAt, rowid: rowid, ), @@ -2243,11 +3612,13 @@ class $$GroupTableTableTableManager ({ required String id, required String name, + required String description, required DateTime createdAt, Value rowid = const Value.absent(), }) => GroupTableCompanion.insert( id: id, name: name, + description: description, createdAt: createdAt, rowid: rowid, ), @@ -2260,16 +3631,37 @@ class $$GroupTableTableTableManager ) .toList(), prefetchHooksCallback: - ({playerGroupTableRefs = false, groupMatchTableRefs = false}) { + ({matchTableRefs = false, playerGroupTableRefs = false}) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ + if (matchTableRefs) db.matchTable, if (playerGroupTableRefs) db.playerGroupTable, - if (groupMatchTableRefs) db.groupMatchTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { return [ + if (matchTableRefs) + await $_getPrefetchedData< + GroupTableData, + $GroupTableTable, + MatchTableData + >( + currentTable: table, + referencedTable: $$GroupTableTableReferences + ._matchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupTableTableReferences( + db, + table, + p0, + ).matchTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.groupId == item.id, + ), + typedResults: items, + ), if (playerGroupTableRefs) await $_getPrefetchedData< GroupTableData, @@ -2291,27 +3683,6 @@ class $$GroupTableTableTableManager ), typedResults: items, ), - if (groupMatchTableRefs) - await $_getPrefetchedData< - GroupTableData, - $GroupTableTable, - GroupMatchTableData - >( - currentTable: table, - referencedTable: $$GroupTableTableReferences - ._groupMatchTableRefsTable(db), - managerFromTypedResult: (p0) => - $$GroupTableTableReferences( - db, - table, - p0, - ).groupMatchTableRefs, - referencedItemsForCurrentItem: - (item, referencedItems) => referencedItems.where( - (e) => e.groupId == item.id, - ), - typedResults: items, - ), ]; }, ); @@ -2332,25 +3703,369 @@ typedef $$GroupTableTableProcessedTableManager = $$GroupTableTableUpdateCompanionBuilder, (GroupTableData, $$GroupTableTableReferences), GroupTableData, - PrefetchHooks Function({ - bool playerGroupTableRefs, - bool groupMatchTableRefs, - }) + PrefetchHooks Function({bool matchTableRefs, bool playerGroupTableRefs}) + >; +typedef $$GameTableTableCreateCompanionBuilder = + GameTableCompanion Function({ + required String id, + required String name, + required String ruleset, + required String description, + required String color, + required String icon, + required DateTime createdAt, + Value rowid, + }); +typedef $$GameTableTableUpdateCompanionBuilder = + GameTableCompanion Function({ + Value id, + Value name, + Value ruleset, + Value description, + Value color, + Value icon, + Value createdAt, + Value rowid, + }); + +final class $$GameTableTableReferences + extends BaseReferences<_$AppDatabase, $GameTableTable, GameTableData> { + $$GameTableTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static MultiTypedResultKey<$MatchTableTable, List> + _matchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.matchTable, + aliasName: $_aliasNameGenerator(db.gameTable.id, db.matchTable.gameId), + ); + + $$MatchTableTableProcessedTableManager get matchTableRefs { + final manager = $$MatchTableTableTableManager( + $_db, + $_db.matchTable, + ).filter((f) => f.gameId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull(_matchTableRefsTable($_db)); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$GameTableTableFilterComposer + extends Composer<_$AppDatabase, $GameTableTable> { + $$GameTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get ruleset => $composableBuilder( + column: $table.ruleset, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get color => $composableBuilder( + column: $table.color, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get icon => $composableBuilder( + column: $table.icon, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnFilters(column), + ); + + Expression matchTableRefs( + Expression Function($$MatchTableTableFilterComposer f) f, + ) { + final $$MatchTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableFilterComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$GameTableTableOrderingComposer + extends Composer<_$AppDatabase, $GameTableTable> { + $$GameTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get ruleset => $composableBuilder( + column: $table.ruleset, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get description => $composableBuilder( + column: $table.description, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get color => $composableBuilder( + column: $table.color, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get icon => $composableBuilder( + column: $table.icon, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$GameTableTableAnnotationComposer + extends Composer<_$AppDatabase, $GameTableTable> { + $$GameTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get ruleset => + $composableBuilder(column: $table.ruleset, builder: (column) => column); + + GeneratedColumn get description => $composableBuilder( + column: $table.description, + builder: (column) => column, + ); + + GeneratedColumn get color => + $composableBuilder(column: $table.color, builder: (column) => column); + + GeneratedColumn get icon => + $composableBuilder(column: $table.icon, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + Expression matchTableRefs( + Expression Function($$MatchTableTableAnnotationComposer a) f, + ) { + final $$MatchTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableAnnotationComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$GameTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $GameTableTable, + GameTableData, + $$GameTableTableFilterComposer, + $$GameTableTableOrderingComposer, + $$GameTableTableAnnotationComposer, + $$GameTableTableCreateCompanionBuilder, + $$GameTableTableUpdateCompanionBuilder, + (GameTableData, $$GameTableTableReferences), + GameTableData, + PrefetchHooks Function({bool matchTableRefs}) + > { + $$GameTableTableTableManager(_$AppDatabase db, $GameTableTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$GameTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$GameTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$GameTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value ruleset = const Value.absent(), + Value description = const Value.absent(), + Value color = const Value.absent(), + Value icon = const Value.absent(), + Value createdAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => GameTableCompanion( + id: id, + name: name, + ruleset: ruleset, + description: description, + color: color, + icon: icon, + createdAt: createdAt, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required String name, + required String ruleset, + required String description, + required String color, + required String icon, + required DateTime createdAt, + Value rowid = const Value.absent(), + }) => GameTableCompanion.insert( + id: id, + name: name, + ruleset: ruleset, + description: description, + color: color, + icon: icon, + createdAt: createdAt, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$GameTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({matchTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [if (matchTableRefs) db.matchTable], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (matchTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + MatchTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._matchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).matchTableRefs, + referencedItemsForCurrentItem: (item, referencedItems) => + referencedItems.where((e) => e.gameId == item.id), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$GameTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $GameTableTable, + GameTableData, + $$GameTableTableFilterComposer, + $$GameTableTableOrderingComposer, + $$GameTableTableAnnotationComposer, + $$GameTableTableCreateCompanionBuilder, + $$GameTableTableUpdateCompanionBuilder, + (GameTableData, $$GameTableTableReferences), + GameTableData, + PrefetchHooks Function({bool matchTableRefs}) >; typedef $$MatchTableTableCreateCompanionBuilder = MatchTableCompanion Function({ - Value winnerId, required String id, - required String name, + required String gameId, + Value groupId, + Value name, + Value notes, required DateTime createdAt, + Value endedAt, Value rowid, }); typedef $$MatchTableTableUpdateCompanionBuilder = MatchTableCompanion Function({ - Value winnerId, Value id, - Value name, + Value gameId, + Value groupId, + Value name, + Value notes, Value createdAt, + Value endedAt, Value rowid, }); @@ -2358,6 +4073,42 @@ final class $$MatchTableTableReferences extends BaseReferences<_$AppDatabase, $MatchTableTable, MatchTableData> { $$MatchTableTableReferences(super.$_db, super.$_table, super.$_typedResult); + static $GameTableTable _gameIdTable(_$AppDatabase db) => db.gameTable + .createAlias($_aliasNameGenerator(db.matchTable.gameId, db.gameTable.id)); + + $$GameTableTableProcessedTableManager get gameId { + final $_column = $_itemColumn('game_id')!; + + final manager = $$GameTableTableTableManager( + $_db, + $_db.gameTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_gameIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GroupTableTable _groupIdTable(_$AppDatabase db) => + db.groupTable.createAlias( + $_aliasNameGenerator(db.matchTable.groupId, db.groupTable.id), + ); + + $$GroupTableTableProcessedTableManager? get groupId { + final $_column = $_itemColumn('group_id'); + if ($_column == null) return null; + final manager = $$GroupTableTableTableManager( + $_db, + $_db.groupTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + static MultiTypedResultKey<$PlayerMatchTableTable, List> _playerMatchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( db.playerMatchTable, @@ -2381,24 +4132,19 @@ final class $$MatchTableTableReferences ); } - static MultiTypedResultKey<$GroupMatchTableTable, List> - _groupMatchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( - db.groupMatchTable, - aliasName: $_aliasNameGenerator( - db.matchTable.id, - db.groupMatchTable.matchId, - ), + static MultiTypedResultKey<$ScoreTableTable, List> + _scoreTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.scoreTable, + aliasName: $_aliasNameGenerator(db.matchTable.id, db.scoreTable.matchId), ); - $$GroupMatchTableTableProcessedTableManager get groupMatchTableRefs { - final manager = $$GroupMatchTableTableTableManager( + $$ScoreTableTableProcessedTableManager get scoreTableRefs { + final manager = $$ScoreTableTableTableManager( $_db, - $_db.groupMatchTable, + $_db.scoreTable, ).filter((f) => f.matchId.id.sqlEquals($_itemColumn('id')!)); - final cache = $_typedResult.readTableOrNull( - _groupMatchTableRefsTable($_db), - ); + final cache = $_typedResult.readTableOrNull(_scoreTableRefsTable($_db)); return ProcessedTableManager( manager.$state.copyWith(prefetchedData: cache), ); @@ -2414,11 +4160,6 @@ class $$MatchTableTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnFilters get winnerId => $composableBuilder( - column: $table.winnerId, - builder: (column) => ColumnFilters(column), - ); - ColumnFilters get id => $composableBuilder( column: $table.id, builder: (column) => ColumnFilters(column), @@ -2429,11 +4170,67 @@ class $$MatchTableTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get notes => $composableBuilder( + column: $table.notes, + builder: (column) => ColumnFilters(column), + ); + ColumnFilters get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnFilters(column), ); + ColumnFilters get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnFilters(column), + ); + + $$GameTableTableFilterComposer get gameId { + final $$GameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableFilterComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableFilterComposer get groupId { + final $$GroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableFilterComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + Expression playerMatchTableRefs( Expression Function($$PlayerMatchTableTableFilterComposer f) f, ) { @@ -2459,22 +4256,22 @@ class $$MatchTableTableFilterComposer return f(composer); } - Expression groupMatchTableRefs( - Expression Function($$GroupMatchTableTableFilterComposer f) f, + Expression scoreTableRefs( + Expression Function($$ScoreTableTableFilterComposer f) f, ) { - final $$GroupMatchTableTableFilterComposer composer = $composerBuilder( + final $$ScoreTableTableFilterComposer composer = $composerBuilder( composer: this, getCurrentColumn: (t) => t.id, - referencedTable: $db.groupMatchTable, + referencedTable: $db.scoreTable, getReferencedColumn: (t) => t.matchId, builder: ( joinBuilder, { $addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer, - }) => $$GroupMatchTableTableFilterComposer( + }) => $$ScoreTableTableFilterComposer( $db: $db, - $table: $db.groupMatchTable, + $table: $db.scoreTable, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: @@ -2494,11 +4291,6 @@ class $$MatchTableTableOrderingComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - ColumnOrderings get winnerId => $composableBuilder( - column: $table.winnerId, - builder: (column) => ColumnOrderings(column), - ); - ColumnOrderings get id => $composableBuilder( column: $table.id, builder: (column) => ColumnOrderings(column), @@ -2509,10 +4301,66 @@ class $$MatchTableTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get notes => $composableBuilder( + column: $table.notes, + builder: (column) => ColumnOrderings(column), + ); + ColumnOrderings get createdAt => $composableBuilder( column: $table.createdAt, builder: (column) => ColumnOrderings(column), ); + + ColumnOrderings get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnOrderings(column), + ); + + $$GameTableTableOrderingComposer get gameId { + final $$GameTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableOrderingComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableOrderingComposer get groupId { + final $$GroupTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableOrderingComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } } class $$MatchTableTableAnnotationComposer @@ -2524,18 +4372,67 @@ class $$MatchTableTableAnnotationComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); - GeneratedColumn get winnerId => - $composableBuilder(column: $table.winnerId, builder: (column) => column); - GeneratedColumn get id => $composableBuilder(column: $table.id, builder: (column) => column); GeneratedColumn get name => $composableBuilder(column: $table.name, builder: (column) => column); + GeneratedColumn get notes => + $composableBuilder(column: $table.notes, builder: (column) => column); + GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); + GeneratedColumn get endedAt => + $composableBuilder(column: $table.endedAt, builder: (column) => column); + + $$GameTableTableAnnotationComposer get gameId { + final $$GameTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.gameId, + referencedTable: $db.gameTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GameTableTableAnnotationComposer( + $db: $db, + $table: $db.gameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$GroupTableTableAnnotationComposer get groupId { + final $$GroupTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.groupId, + referencedTable: $db.groupTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$GroupTableTableAnnotationComposer( + $db: $db, + $table: $db.groupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + Expression playerMatchTableRefs( Expression Function($$PlayerMatchTableTableAnnotationComposer a) f, ) { @@ -2561,22 +4458,22 @@ class $$MatchTableTableAnnotationComposer return f(composer); } - Expression groupMatchTableRefs( - Expression Function($$GroupMatchTableTableAnnotationComposer a) f, + Expression scoreTableRefs( + Expression Function($$ScoreTableTableAnnotationComposer a) f, ) { - final $$GroupMatchTableTableAnnotationComposer composer = $composerBuilder( + final $$ScoreTableTableAnnotationComposer composer = $composerBuilder( composer: this, getCurrentColumn: (t) => t.id, - referencedTable: $db.groupMatchTable, + referencedTable: $db.scoreTable, getReferencedColumn: (t) => t.matchId, builder: ( joinBuilder, { $addJoinBuilderToRootComposer, $removeJoinBuilderFromRootComposer, - }) => $$GroupMatchTableTableAnnotationComposer( + }) => $$ScoreTableTableAnnotationComposer( $db: $db, - $table: $db.groupMatchTable, + $table: $db.scoreTable, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, joinBuilder: joinBuilder, $removeJoinBuilderFromRootComposer: @@ -2601,8 +4498,10 @@ class $$MatchTableTableTableManager (MatchTableData, $$MatchTableTableReferences), MatchTableData, PrefetchHooks Function({ + bool gameId, + bool groupId, bool playerMatchTableRefs, - bool groupMatchTableRefs, + bool scoreTableRefs, }) > { $$MatchTableTableTableManager(_$AppDatabase db, $MatchTableTable table) @@ -2618,30 +4517,42 @@ class $$MatchTableTableTableManager $$MatchTableTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ - Value winnerId = const Value.absent(), Value id = const Value.absent(), - Value name = const Value.absent(), + Value gameId = const Value.absent(), + Value groupId = const Value.absent(), + Value name = const Value.absent(), + Value notes = const Value.absent(), Value createdAt = const Value.absent(), + Value endedAt = const Value.absent(), Value rowid = const Value.absent(), }) => MatchTableCompanion( - winnerId: winnerId, id: id, + gameId: gameId, + groupId: groupId, name: name, + notes: notes, createdAt: createdAt, + endedAt: endedAt, rowid: rowid, ), createCompanionCallback: ({ - Value winnerId = const Value.absent(), required String id, - required String name, + required String gameId, + Value groupId = const Value.absent(), + Value name = const Value.absent(), + Value notes = const Value.absent(), required DateTime createdAt, + Value endedAt = const Value.absent(), Value rowid = const Value.absent(), }) => MatchTableCompanion.insert( - winnerId: winnerId, id: id, + gameId: gameId, + groupId: groupId, name: name, + notes: notes, createdAt: createdAt, + endedAt: endedAt, rowid: rowid, ), withReferenceMapper: (p0) => p0 @@ -2653,14 +4564,65 @@ class $$MatchTableTableTableManager ) .toList(), prefetchHooksCallback: - ({playerMatchTableRefs = false, groupMatchTableRefs = false}) { + ({ + gameId = false, + groupId = false, + playerMatchTableRefs = false, + scoreTableRefs = false, + }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (playerMatchTableRefs) db.playerMatchTable, - if (groupMatchTableRefs) db.groupMatchTable, + if (scoreTableRefs) db.scoreTable, ], - addJoins: null, + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (gameId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.gameId, + referencedTable: $$MatchTableTableReferences + ._gameIdTable(db), + referencedColumn: + $$MatchTableTableReferences + ._gameIdTable(db) + .id, + ) + as T; + } + if (groupId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: $$MatchTableTableReferences + ._groupIdTable(db), + referencedColumn: + $$MatchTableTableReferences + ._groupIdTable(db) + .id, + ) + as T; + } + + return state; + }, getPrefetchedDataCallback: (items) async { return [ if (playerMatchTableRefs) @@ -2684,21 +4646,21 @@ class $$MatchTableTableTableManager ), typedResults: items, ), - if (groupMatchTableRefs) + if (scoreTableRefs) await $_getPrefetchedData< MatchTableData, $MatchTableTable, - GroupMatchTableData + ScoreTableData >( currentTable: table, referencedTable: $$MatchTableTableReferences - ._groupMatchTableRefsTable(db), + ._scoreTableRefsTable(db), managerFromTypedResult: (p0) => $$MatchTableTableReferences( db, table, p0, - ).groupMatchTableRefs, + ).scoreTableRefs, referencedItemsForCurrentItem: (item, referencedItems) => referencedItems.where( (e) => e.matchId == item.id, @@ -2726,8 +4688,10 @@ typedef $$MatchTableTableProcessedTableManager = (MatchTableData, $$MatchTableTableReferences), MatchTableData, PrefetchHooks Function({ + bool gameId, + bool groupId, bool playerMatchTableRefs, - bool groupMatchTableRefs, + bool scoreTableRefs, }) >; typedef $$PlayerGroupTableTableCreateCompanionBuilder = @@ -3095,16 +5059,290 @@ typedef $$PlayerGroupTableTableProcessedTableManager = PlayerGroupTableData, PrefetchHooks Function({bool playerId, bool groupId}) >; +typedef $$TeamTableTableCreateCompanionBuilder = + TeamTableCompanion Function({ + required String id, + required String name, + required DateTime createdAt, + Value rowid, + }); +typedef $$TeamTableTableUpdateCompanionBuilder = + TeamTableCompanion Function({ + Value id, + Value name, + Value createdAt, + Value rowid, + }); + +final class $$TeamTableTableReferences + extends BaseReferences<_$AppDatabase, $TeamTableTable, TeamTableData> { + $$TeamTableTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static MultiTypedResultKey<$PlayerMatchTableTable, List> + _playerMatchTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( + db.playerMatchTable, + aliasName: $_aliasNameGenerator( + db.teamTable.id, + db.playerMatchTable.teamId, + ), + ); + + $$PlayerMatchTableTableProcessedTableManager get playerMatchTableRefs { + final manager = $$PlayerMatchTableTableTableManager( + $_db, + $_db.playerMatchTable, + ).filter((f) => f.teamId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _playerMatchTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$TeamTableTableFilterComposer + extends Composer<_$AppDatabase, $TeamTableTable> { + $$TeamTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnFilters(column), + ); + + Expression playerMatchTableRefs( + Expression Function($$PlayerMatchTableTableFilterComposer f) f, + ) { + final $$PlayerMatchTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.playerMatchTable, + getReferencedColumn: (t) => t.teamId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$PlayerMatchTableTableFilterComposer( + $db: $db, + $table: $db.playerMatchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$TeamTableTableOrderingComposer + extends Composer<_$AppDatabase, $TeamTableTable> { + $$TeamTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get id => $composableBuilder( + column: $table.id, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get name => $composableBuilder( + column: $table.name, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get createdAt => $composableBuilder( + column: $table.createdAt, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$TeamTableTableAnnotationComposer + extends Composer<_$AppDatabase, $TeamTableTable> { + $$TeamTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get name => + $composableBuilder(column: $table.name, builder: (column) => column); + + GeneratedColumn get createdAt => + $composableBuilder(column: $table.createdAt, builder: (column) => column); + + Expression playerMatchTableRefs( + Expression Function($$PlayerMatchTableTableAnnotationComposer a) f, + ) { + final $$PlayerMatchTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.playerMatchTable, + getReferencedColumn: (t) => t.teamId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$PlayerMatchTableTableAnnotationComposer( + $db: $db, + $table: $db.playerMatchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$TeamTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $TeamTableTable, + TeamTableData, + $$TeamTableTableFilterComposer, + $$TeamTableTableOrderingComposer, + $$TeamTableTableAnnotationComposer, + $$TeamTableTableCreateCompanionBuilder, + $$TeamTableTableUpdateCompanionBuilder, + (TeamTableData, $$TeamTableTableReferences), + TeamTableData, + PrefetchHooks Function({bool playerMatchTableRefs}) + > { + $$TeamTableTableTableManager(_$AppDatabase db, $TeamTableTable table) + : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$TeamTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$TeamTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$TeamTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value name = const Value.absent(), + Value createdAt = const Value.absent(), + Value rowid = const Value.absent(), + }) => TeamTableCompanion( + id: id, + name: name, + createdAt: createdAt, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required String name, + required DateTime createdAt, + Value rowid = const Value.absent(), + }) => TeamTableCompanion.insert( + id: id, + name: name, + createdAt: createdAt, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$TeamTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({playerMatchTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (playerMatchTableRefs) db.playerMatchTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (playerMatchTableRefs) + await $_getPrefetchedData< + TeamTableData, + $TeamTableTable, + PlayerMatchTableData + >( + currentTable: table, + referencedTable: $$TeamTableTableReferences + ._playerMatchTableRefsTable(db), + managerFromTypedResult: (p0) => + $$TeamTableTableReferences( + db, + table, + p0, + ).playerMatchTableRefs, + referencedItemsForCurrentItem: (item, referencedItems) => + referencedItems.where((e) => e.teamId == item.id), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$TeamTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $TeamTableTable, + TeamTableData, + $$TeamTableTableFilterComposer, + $$TeamTableTableOrderingComposer, + $$TeamTableTableAnnotationComposer, + $$TeamTableTableCreateCompanionBuilder, + $$TeamTableTableUpdateCompanionBuilder, + (TeamTableData, $$TeamTableTableReferences), + TeamTableData, + PrefetchHooks Function({bool playerMatchTableRefs}) + >; typedef $$PlayerMatchTableTableCreateCompanionBuilder = PlayerMatchTableCompanion Function({ required String playerId, required String matchId, + Value teamId, + required int score, Value rowid, }); typedef $$PlayerMatchTableTableUpdateCompanionBuilder = PlayerMatchTableCompanion Function({ Value playerId, Value matchId, + Value teamId, + Value score, Value rowid, }); @@ -3158,6 +5396,25 @@ final class $$PlayerMatchTableTableReferences manager.$state.copyWith(prefetchedData: [item]), ); } + + static $TeamTableTable _teamIdTable(_$AppDatabase db) => + db.teamTable.createAlias( + $_aliasNameGenerator(db.playerMatchTable.teamId, db.teamTable.id), + ); + + $$TeamTableTableProcessedTableManager? get teamId { + final $_column = $_itemColumn('team_id'); + if ($_column == null) return null; + final manager = $$TeamTableTableTableManager( + $_db, + $_db.teamTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_teamIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } } class $$PlayerMatchTableTableFilterComposer @@ -3169,6 +5426,489 @@ class $$PlayerMatchTableTableFilterComposer super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnFilters get score => $composableBuilder( + column: $table.score, + builder: (column) => ColumnFilters(column), + ); + + $$PlayerTableTableFilterComposer get playerId { + final $$PlayerTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.playerId, + referencedTable: $db.playerTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$PlayerTableTableFilterComposer( + $db: $db, + $table: $db.playerTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$MatchTableTableFilterComposer get matchId { + final $$MatchTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.matchId, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableFilterComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$TeamTableTableFilterComposer get teamId { + final $$TeamTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.teamId, + referencedTable: $db.teamTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TeamTableTableFilterComposer( + $db: $db, + $table: $db.teamTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PlayerMatchTableTableOrderingComposer + extends Composer<_$AppDatabase, $PlayerMatchTableTable> { + $$PlayerMatchTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get score => $composableBuilder( + column: $table.score, + builder: (column) => ColumnOrderings(column), + ); + + $$PlayerTableTableOrderingComposer get playerId { + final $$PlayerTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.playerId, + referencedTable: $db.playerTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$PlayerTableTableOrderingComposer( + $db: $db, + $table: $db.playerTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$MatchTableTableOrderingComposer get matchId { + final $$MatchTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.matchId, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableOrderingComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$TeamTableTableOrderingComposer get teamId { + final $$TeamTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.teamId, + referencedTable: $db.teamTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TeamTableTableOrderingComposer( + $db: $db, + $table: $db.teamTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PlayerMatchTableTableAnnotationComposer + extends Composer<_$AppDatabase, $PlayerMatchTableTable> { + $$PlayerMatchTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get score => + $composableBuilder(column: $table.score, builder: (column) => column); + + $$PlayerTableTableAnnotationComposer get playerId { + final $$PlayerTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.playerId, + referencedTable: $db.playerTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$PlayerTableTableAnnotationComposer( + $db: $db, + $table: $db.playerTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$MatchTableTableAnnotationComposer get matchId { + final $$MatchTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.matchId, + referencedTable: $db.matchTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$MatchTableTableAnnotationComposer( + $db: $db, + $table: $db.matchTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$TeamTableTableAnnotationComposer get teamId { + final $$TeamTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.teamId, + referencedTable: $db.teamTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$TeamTableTableAnnotationComposer( + $db: $db, + $table: $db.teamTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$PlayerMatchTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $PlayerMatchTableTable, + PlayerMatchTableData, + $$PlayerMatchTableTableFilterComposer, + $$PlayerMatchTableTableOrderingComposer, + $$PlayerMatchTableTableAnnotationComposer, + $$PlayerMatchTableTableCreateCompanionBuilder, + $$PlayerMatchTableTableUpdateCompanionBuilder, + (PlayerMatchTableData, $$PlayerMatchTableTableReferences), + PlayerMatchTableData, + PrefetchHooks Function({bool playerId, bool matchId, bool teamId}) + > { + $$PlayerMatchTableTableTableManager( + _$AppDatabase db, + $PlayerMatchTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$PlayerMatchTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$PlayerMatchTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$PlayerMatchTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value playerId = const Value.absent(), + Value matchId = const Value.absent(), + Value teamId = const Value.absent(), + Value score = const Value.absent(), + Value rowid = const Value.absent(), + }) => PlayerMatchTableCompanion( + playerId: playerId, + matchId: matchId, + teamId: teamId, + score: score, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String playerId, + required String matchId, + Value teamId = const Value.absent(), + required int score, + Value rowid = const Value.absent(), + }) => PlayerMatchTableCompanion.insert( + playerId: playerId, + matchId: matchId, + teamId: teamId, + score: score, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$PlayerMatchTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: + ({playerId = false, matchId = false, teamId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (playerId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.playerId, + referencedTable: + $$PlayerMatchTableTableReferences + ._playerIdTable(db), + referencedColumn: + $$PlayerMatchTableTableReferences + ._playerIdTable(db) + .id, + ) + as T; + } + if (matchId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.matchId, + referencedTable: + $$PlayerMatchTableTableReferences + ._matchIdTable(db), + referencedColumn: + $$PlayerMatchTableTableReferences + ._matchIdTable(db) + .id, + ) + as T; + } + if (teamId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.teamId, + referencedTable: + $$PlayerMatchTableTableReferences + ._teamIdTable(db), + referencedColumn: + $$PlayerMatchTableTableReferences + ._teamIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$PlayerMatchTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $PlayerMatchTableTable, + PlayerMatchTableData, + $$PlayerMatchTableTableFilterComposer, + $$PlayerMatchTableTableOrderingComposer, + $$PlayerMatchTableTableAnnotationComposer, + $$PlayerMatchTableTableCreateCompanionBuilder, + $$PlayerMatchTableTableUpdateCompanionBuilder, + (PlayerMatchTableData, $$PlayerMatchTableTableReferences), + PlayerMatchTableData, + PrefetchHooks Function({bool playerId, bool matchId, bool teamId}) + >; +typedef $$ScoreTableTableCreateCompanionBuilder = + ScoreTableCompanion Function({ + required String playerId, + required String matchId, + required int roundNumber, + required int score, + required int change, + Value rowid, + }); +typedef $$ScoreTableTableUpdateCompanionBuilder = + ScoreTableCompanion Function({ + Value playerId, + Value matchId, + Value roundNumber, + Value score, + Value change, + Value rowid, + }); + +final class $$ScoreTableTableReferences + extends BaseReferences<_$AppDatabase, $ScoreTableTable, ScoreTableData> { + $$ScoreTableTableReferences(super.$_db, super.$_table, super.$_typedResult); + + static $PlayerTableTable _playerIdTable(_$AppDatabase db) => + db.playerTable.createAlias( + $_aliasNameGenerator(db.scoreTable.playerId, db.playerTable.id), + ); + + $$PlayerTableTableProcessedTableManager get playerId { + final $_column = $_itemColumn('player_id')!; + + final manager = $$PlayerTableTableTableManager( + $_db, + $_db.playerTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_playerIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $MatchTableTable _matchIdTable(_$AppDatabase db) => + db.matchTable.createAlias( + $_aliasNameGenerator(db.scoreTable.matchId, db.matchTable.id), + ); + + $$MatchTableTableProcessedTableManager get matchId { + final $_column = $_itemColumn('match_id')!; + + final manager = $$MatchTableTableTableManager( + $_db, + $_db.matchTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_matchIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$ScoreTableTableFilterComposer + extends Composer<_$AppDatabase, $ScoreTableTable> { + $$ScoreTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get roundNumber => $composableBuilder( + column: $table.roundNumber, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get score => $composableBuilder( + column: $table.score, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get change => $composableBuilder( + column: $table.change, + builder: (column) => ColumnFilters(column), + ); + $$PlayerTableTableFilterComposer get playerId { final $$PlayerTableTableFilterComposer composer = $composerBuilder( composer: this, @@ -3216,15 +5956,30 @@ class $$PlayerMatchTableTableFilterComposer } } -class $$PlayerMatchTableTableOrderingComposer - extends Composer<_$AppDatabase, $PlayerMatchTableTable> { - $$PlayerMatchTableTableOrderingComposer({ +class $$ScoreTableTableOrderingComposer + extends Composer<_$AppDatabase, $ScoreTableTable> { + $$ScoreTableTableOrderingComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + ColumnOrderings get roundNumber => $composableBuilder( + column: $table.roundNumber, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get score => $composableBuilder( + column: $table.score, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get change => $composableBuilder( + column: $table.change, + builder: (column) => ColumnOrderings(column), + ); + $$PlayerTableTableOrderingComposer get playerId { final $$PlayerTableTableOrderingComposer composer = $composerBuilder( composer: this, @@ -3272,15 +6027,26 @@ class $$PlayerMatchTableTableOrderingComposer } } -class $$PlayerMatchTableTableAnnotationComposer - extends Composer<_$AppDatabase, $PlayerMatchTableTable> { - $$PlayerMatchTableTableAnnotationComposer({ +class $$ScoreTableTableAnnotationComposer + extends Composer<_$AppDatabase, $ScoreTableTable> { + $$ScoreTableTableAnnotationComposer({ required super.$db, required super.$table, super.joinBuilder, super.$addJoinBuilderToRootComposer, super.$removeJoinBuilderFromRootComposer, }); + GeneratedColumn get roundNumber => $composableBuilder( + column: $table.roundNumber, + builder: (column) => column, + ); + + GeneratedColumn get score => + $composableBuilder(column: $table.score, builder: (column) => column); + + GeneratedColumn get change => + $composableBuilder(column: $table.change, builder: (column) => column); + $$PlayerTableTableAnnotationComposer get playerId { final $$PlayerTableTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -3328,59 +6094,69 @@ class $$PlayerMatchTableTableAnnotationComposer } } -class $$PlayerMatchTableTableTableManager +class $$ScoreTableTableTableManager extends RootTableManager< _$AppDatabase, - $PlayerMatchTableTable, - PlayerMatchTableData, - $$PlayerMatchTableTableFilterComposer, - $$PlayerMatchTableTableOrderingComposer, - $$PlayerMatchTableTableAnnotationComposer, - $$PlayerMatchTableTableCreateCompanionBuilder, - $$PlayerMatchTableTableUpdateCompanionBuilder, - (PlayerMatchTableData, $$PlayerMatchTableTableReferences), - PlayerMatchTableData, + $ScoreTableTable, + ScoreTableData, + $$ScoreTableTableFilterComposer, + $$ScoreTableTableOrderingComposer, + $$ScoreTableTableAnnotationComposer, + $$ScoreTableTableCreateCompanionBuilder, + $$ScoreTableTableUpdateCompanionBuilder, + (ScoreTableData, $$ScoreTableTableReferences), + ScoreTableData, PrefetchHooks Function({bool playerId, bool matchId}) > { - $$PlayerMatchTableTableTableManager( - _$AppDatabase db, - $PlayerMatchTableTable table, - ) : super( + $$ScoreTableTableTableManager(_$AppDatabase db, $ScoreTableTable table) + : super( TableManagerState( db: db, table: table, createFilteringComposer: () => - $$PlayerMatchTableTableFilterComposer($db: db, $table: table), + $$ScoreTableTableFilterComposer($db: db, $table: table), createOrderingComposer: () => - $$PlayerMatchTableTableOrderingComposer($db: db, $table: table), + $$ScoreTableTableOrderingComposer($db: db, $table: table), createComputedFieldComposer: () => - $$PlayerMatchTableTableAnnotationComposer($db: db, $table: table), + $$ScoreTableTableAnnotationComposer($db: db, $table: table), updateCompanionCallback: ({ Value playerId = const Value.absent(), Value matchId = const Value.absent(), + Value roundNumber = const Value.absent(), + Value score = const Value.absent(), + Value change = const Value.absent(), Value rowid = const Value.absent(), - }) => PlayerMatchTableCompanion( + }) => ScoreTableCompanion( playerId: playerId, matchId: matchId, + roundNumber: roundNumber, + score: score, + change: change, rowid: rowid, ), createCompanionCallback: ({ required String playerId, required String matchId, + required int roundNumber, + required int score, + required int change, Value rowid = const Value.absent(), - }) => PlayerMatchTableCompanion.insert( + }) => ScoreTableCompanion.insert( playerId: playerId, matchId: matchId, + roundNumber: roundNumber, + score: score, + change: change, rowid: rowid, ), withReferenceMapper: (p0) => p0 .map( (e) => ( e.readTable(table), - $$PlayerMatchTableTableReferences(db, table, e), + $$ScoreTableTableReferences(db, table, e), ), ) .toList(), @@ -3409,13 +6185,11 @@ class $$PlayerMatchTableTableTableManager state.withJoin( currentTable: table, currentColumn: table.playerId, - referencedTable: - $$PlayerMatchTableTableReferences - ._playerIdTable(db), - referencedColumn: - $$PlayerMatchTableTableReferences - ._playerIdTable(db) - .id, + referencedTable: $$ScoreTableTableReferences + ._playerIdTable(db), + referencedColumn: $$ScoreTableTableReferences + ._playerIdTable(db) + .id, ) as T; } @@ -3424,13 +6198,11 @@ class $$PlayerMatchTableTableTableManager state.withJoin( currentTable: table, currentColumn: table.matchId, - referencedTable: - $$PlayerMatchTableTableReferences - ._matchIdTable(db), - referencedColumn: - $$PlayerMatchTableTableReferences - ._matchIdTable(db) - .id, + referencedTable: $$ScoreTableTableReferences + ._matchIdTable(db), + referencedColumn: $$ScoreTableTableReferences + ._matchIdTable(db) + .id, ) as T; } @@ -3446,385 +6218,20 @@ class $$PlayerMatchTableTableTableManager ); } -typedef $$PlayerMatchTableTableProcessedTableManager = +typedef $$ScoreTableTableProcessedTableManager = ProcessedTableManager< _$AppDatabase, - $PlayerMatchTableTable, - PlayerMatchTableData, - $$PlayerMatchTableTableFilterComposer, - $$PlayerMatchTableTableOrderingComposer, - $$PlayerMatchTableTableAnnotationComposer, - $$PlayerMatchTableTableCreateCompanionBuilder, - $$PlayerMatchTableTableUpdateCompanionBuilder, - (PlayerMatchTableData, $$PlayerMatchTableTableReferences), - PlayerMatchTableData, + $ScoreTableTable, + ScoreTableData, + $$ScoreTableTableFilterComposer, + $$ScoreTableTableOrderingComposer, + $$ScoreTableTableAnnotationComposer, + $$ScoreTableTableCreateCompanionBuilder, + $$ScoreTableTableUpdateCompanionBuilder, + (ScoreTableData, $$ScoreTableTableReferences), + ScoreTableData, PrefetchHooks Function({bool playerId, bool matchId}) >; -typedef $$GroupMatchTableTableCreateCompanionBuilder = - GroupMatchTableCompanion Function({ - required String groupId, - required String matchId, - Value rowid, - }); -typedef $$GroupMatchTableTableUpdateCompanionBuilder = - GroupMatchTableCompanion Function({ - Value groupId, - Value matchId, - Value rowid, - }); - -final class $$GroupMatchTableTableReferences - extends - BaseReferences< - _$AppDatabase, - $GroupMatchTableTable, - GroupMatchTableData - > { - $$GroupMatchTableTableReferences( - super.$_db, - super.$_table, - super.$_typedResult, - ); - - static $GroupTableTable _groupIdTable(_$AppDatabase db) => - db.groupTable.createAlias( - $_aliasNameGenerator(db.groupMatchTable.groupId, db.groupTable.id), - ); - - $$GroupTableTableProcessedTableManager get groupId { - final $_column = $_itemColumn('group_id')!; - - final manager = $$GroupTableTableTableManager( - $_db, - $_db.groupTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_groupIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } - - static $MatchTableTable _matchIdTable(_$AppDatabase db) => - db.matchTable.createAlias( - $_aliasNameGenerator(db.groupMatchTable.matchId, db.matchTable.id), - ); - - $$MatchTableTableProcessedTableManager get matchId { - final $_column = $_itemColumn('match_id')!; - - final manager = $$MatchTableTableTableManager( - $_db, - $_db.matchTable, - ).filter((f) => f.id.sqlEquals($_column)); - final item = $_typedResult.readTableOrNull(_matchIdTable($_db)); - if (item == null) return manager; - return ProcessedTableManager( - manager.$state.copyWith(prefetchedData: [item]), - ); - } -} - -class $$GroupMatchTableTableFilterComposer - extends Composer<_$AppDatabase, $GroupMatchTableTable> { - $$GroupMatchTableTableFilterComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$GroupTableTableFilterComposer get groupId { - final $$GroupTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableFilterComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$MatchTableTableFilterComposer get matchId { - final $$MatchTableTableFilterComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.matchId, - referencedTable: $db.matchTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$MatchTableTableFilterComposer( - $db: $db, - $table: $db.matchTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$GroupMatchTableTableOrderingComposer - extends Composer<_$AppDatabase, $GroupMatchTableTable> { - $$GroupMatchTableTableOrderingComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$GroupTableTableOrderingComposer get groupId { - final $$GroupTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableOrderingComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$MatchTableTableOrderingComposer get matchId { - final $$MatchTableTableOrderingComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.matchId, - referencedTable: $db.matchTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$MatchTableTableOrderingComposer( - $db: $db, - $table: $db.matchTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$GroupMatchTableTableAnnotationComposer - extends Composer<_$AppDatabase, $GroupMatchTableTable> { - $$GroupMatchTableTableAnnotationComposer({ - required super.$db, - required super.$table, - super.joinBuilder, - super.$addJoinBuilderToRootComposer, - super.$removeJoinBuilderFromRootComposer, - }); - $$GroupTableTableAnnotationComposer get groupId { - final $$GroupTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.groupId, - referencedTable: $db.groupTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$GroupTableTableAnnotationComposer( - $db: $db, - $table: $db.groupTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } - - $$MatchTableTableAnnotationComposer get matchId { - final $$MatchTableTableAnnotationComposer composer = $composerBuilder( - composer: this, - getCurrentColumn: (t) => t.matchId, - referencedTable: $db.matchTable, - getReferencedColumn: (t) => t.id, - builder: - ( - joinBuilder, { - $addJoinBuilderToRootComposer, - $removeJoinBuilderFromRootComposer, - }) => $$MatchTableTableAnnotationComposer( - $db: $db, - $table: $db.matchTable, - $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, - joinBuilder: joinBuilder, - $removeJoinBuilderFromRootComposer: - $removeJoinBuilderFromRootComposer, - ), - ); - return composer; - } -} - -class $$GroupMatchTableTableTableManager - extends - RootTableManager< - _$AppDatabase, - $GroupMatchTableTable, - GroupMatchTableData, - $$GroupMatchTableTableFilterComposer, - $$GroupMatchTableTableOrderingComposer, - $$GroupMatchTableTableAnnotationComposer, - $$GroupMatchTableTableCreateCompanionBuilder, - $$GroupMatchTableTableUpdateCompanionBuilder, - (GroupMatchTableData, $$GroupMatchTableTableReferences), - GroupMatchTableData, - PrefetchHooks Function({bool groupId, bool matchId}) - > { - $$GroupMatchTableTableTableManager( - _$AppDatabase db, - $GroupMatchTableTable table, - ) : super( - TableManagerState( - db: db, - table: table, - createFilteringComposer: () => - $$GroupMatchTableTableFilterComposer($db: db, $table: table), - createOrderingComposer: () => - $$GroupMatchTableTableOrderingComposer($db: db, $table: table), - createComputedFieldComposer: () => - $$GroupMatchTableTableAnnotationComposer($db: db, $table: table), - updateCompanionCallback: - ({ - Value groupId = const Value.absent(), - Value matchId = const Value.absent(), - Value rowid = const Value.absent(), - }) => GroupMatchTableCompanion( - groupId: groupId, - matchId: matchId, - rowid: rowid, - ), - createCompanionCallback: - ({ - required String groupId, - required String matchId, - Value rowid = const Value.absent(), - }) => GroupMatchTableCompanion.insert( - groupId: groupId, - matchId: matchId, - rowid: rowid, - ), - withReferenceMapper: (p0) => p0 - .map( - (e) => ( - e.readTable(table), - $$GroupMatchTableTableReferences(db, table, e), - ), - ) - .toList(), - prefetchHooksCallback: ({groupId = false, matchId = false}) { - return PrefetchHooks( - db: db, - explicitlyWatchedTables: [], - addJoins: - < - T extends TableManagerState< - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic, - dynamic - > - >(state) { - if (groupId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.groupId, - referencedTable: - $$GroupMatchTableTableReferences - ._groupIdTable(db), - referencedColumn: - $$GroupMatchTableTableReferences - ._groupIdTable(db) - .id, - ) - as T; - } - if (matchId) { - state = - state.withJoin( - currentTable: table, - currentColumn: table.matchId, - referencedTable: - $$GroupMatchTableTableReferences - ._matchIdTable(db), - referencedColumn: - $$GroupMatchTableTableReferences - ._matchIdTable(db) - .id, - ) - as T; - } - - return state; - }, - getPrefetchedDataCallback: (items) async { - return []; - }, - ); - }, - ), - ); -} - -typedef $$GroupMatchTableTableProcessedTableManager = - ProcessedTableManager< - _$AppDatabase, - $GroupMatchTableTable, - GroupMatchTableData, - $$GroupMatchTableTableFilterComposer, - $$GroupMatchTableTableOrderingComposer, - $$GroupMatchTableTableAnnotationComposer, - $$GroupMatchTableTableCreateCompanionBuilder, - $$GroupMatchTableTableUpdateCompanionBuilder, - (GroupMatchTableData, $$GroupMatchTableTableReferences), - GroupMatchTableData, - PrefetchHooks Function({bool groupId, bool matchId}) - >; class $AppDatabaseManager { final _$AppDatabase _db; @@ -3833,12 +6240,16 @@ class $AppDatabaseManager { $$PlayerTableTableTableManager(_db, _db.playerTable); $$GroupTableTableTableManager get groupTable => $$GroupTableTableTableManager(_db, _db.groupTable); + $$GameTableTableTableManager get gameTable => + $$GameTableTableTableManager(_db, _db.gameTable); $$MatchTableTableTableManager get matchTable => $$MatchTableTableTableManager(_db, _db.matchTable); $$PlayerGroupTableTableTableManager get playerGroupTable => $$PlayerGroupTableTableTableManager(_db, _db.playerGroupTable); + $$TeamTableTableTableManager get teamTable => + $$TeamTableTableTableManager(_db, _db.teamTable); $$PlayerMatchTableTableTableManager get playerMatchTable => $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); - $$GroupMatchTableTableTableManager get groupMatchTable => - $$GroupMatchTableTableTableManager(_db, _db.groupMatchTable); + $$ScoreTableTableTableManager get scoreTable => + $$ScoreTableTableTableManager(_db, _db.scoreTable); } diff --git a/lib/data/db/tables/game_table.dart b/lib/data/db/tables/game_table.dart new file mode 100644 index 0000000..a55b8fc --- /dev/null +++ b/lib/data/db/tables/game_table.dart @@ -0,0 +1,14 @@ +import 'package:drift/drift.dart'; + +class GameTable extends Table { + TextColumn get id => text()(); + TextColumn get name => text()(); + TextColumn get ruleset => text()(); + TextColumn get description => text()(); + TextColumn get color => text()(); + TextColumn get icon => text()(); + DateTimeColumn get createdAt => dateTime()(); + + @override + Set> get primaryKey => {id}; +} diff --git a/lib/data/db/tables/group_match_table.dart b/lib/data/db/tables/group_match_table.dart deleted file mode 100644 index a12e83b..0000000 --- a/lib/data/db/tables/group_match_table.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:drift/drift.dart'; -import 'package:tallee/data/db/tables/group_table.dart'; -import 'package:tallee/data/db/tables/match_table.dart'; - -class GroupMatchTable extends Table { - TextColumn get groupId => - text().references(GroupTable, #id, onDelete: KeyAction.cascade)(); - TextColumn get matchId => - text().references(MatchTable, #id, onDelete: KeyAction.cascade)(); - - @override - Set> get primaryKey => {groupId, matchId}; -} diff --git a/lib/data/db/tables/group_table.dart b/lib/data/db/tables/group_table.dart index 5c52355..2f30cce 100644 --- a/lib/data/db/tables/group_table.dart +++ b/lib/data/db/tables/group_table.dart @@ -3,6 +3,7 @@ import 'package:drift/drift.dart'; class GroupTable extends Table { TextColumn get id => text()(); TextColumn get name => text()(); + TextColumn get description => text()(); DateTimeColumn get createdAt => dateTime()(); @override diff --git a/lib/data/db/tables/match_table.dart b/lib/data/db/tables/match_table.dart index 96aff2a..191e72c 100644 --- a/lib/data/db/tables/match_table.dart +++ b/lib/data/db/tables/match_table.dart @@ -1,11 +1,19 @@ import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; class MatchTable extends Table { TextColumn get id => text()(); - TextColumn get name => text()(); - late final winnerId = text().nullable()(); + TextColumn get gameId => + text().references(GameTable, #id, onDelete: KeyAction.cascade)(); + // Nullable if there is no group associated with the match + TextColumn get groupId => + text().references(GroupTable, #id, onDelete: KeyAction.cascade).nullable()(); + TextColumn get name => text().nullable()(); + TextColumn get notes => text().nullable()(); DateTimeColumn get createdAt => dateTime()(); + DateTimeColumn get endedAt => dateTime().nullable()(); @override Set> get primaryKey => {id}; -} +} \ No newline at end of file diff --git a/lib/data/db/tables/player_match_table.dart b/lib/data/db/tables/player_match_table.dart index 13ef36f..3ff55ea 100644 --- a/lib/data/db/tables/player_match_table.dart +++ b/lib/data/db/tables/player_match_table.dart @@ -1,12 +1,16 @@ import 'package:drift/drift.dart'; import 'package:tallee/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/player_table.dart'; +import 'package:tallee/data/db/tables/team_table.dart'; class PlayerMatchTable extends Table { TextColumn get playerId => text().references(PlayerTable, #id, onDelete: KeyAction.cascade)(); TextColumn get matchId => text().references(MatchTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get teamId => + text().references(TeamTable, #id).nullable()(); + IntColumn get score => integer()(); @override Set> get primaryKey => {playerId, matchId}; diff --git a/lib/data/db/tables/player_table.dart b/lib/data/db/tables/player_table.dart index 794958e..15b29a5 100644 --- a/lib/data/db/tables/player_table.dart +++ b/lib/data/db/tables/player_table.dart @@ -3,6 +3,7 @@ import 'package:drift/drift.dart'; class PlayerTable extends Table { TextColumn get id => text()(); TextColumn get name => text()(); + TextColumn get description => text()(); DateTimeColumn get createdAt => dateTime()(); @override diff --git a/lib/data/db/tables/score_table.dart b/lib/data/db/tables/score_table.dart new file mode 100644 index 0000000..a7bf122 --- /dev/null +++ b/lib/data/db/tables/score_table.dart @@ -0,0 +1,16 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/match_table.dart'; +import 'package:tallee/data/db/tables/player_table.dart'; + +class ScoreTable extends Table { + TextColumn get playerId => + text().references(PlayerTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get matchId => + text().references(MatchTable, #id, onDelete: KeyAction.cascade)(); + IntColumn get roundNumber => integer()(); + IntColumn get score => integer()(); + IntColumn get change => integer()(); + + @override + Set> get primaryKey => {playerId, matchId, roundNumber}; +} \ No newline at end of file diff --git a/lib/data/db/tables/team_table.dart b/lib/data/db/tables/team_table.dart new file mode 100644 index 0000000..b1a24a9 --- /dev/null +++ b/lib/data/db/tables/team_table.dart @@ -0,0 +1,10 @@ +import 'package:drift/drift.dart'; + +class TeamTable extends Table { + TextColumn get id => text()(); + TextColumn get name => text()(); + DateTimeColumn get createdAt => dateTime()(); + + @override + Set> get primaryKey => {id}; +} diff --git a/lib/data/dto/game.dart b/lib/data/dto/game.dart new file mode 100644 index 0000000..2eeee1e --- /dev/null +++ b/lib/data/dto/game.dart @@ -0,0 +1,52 @@ +import 'package:clock/clock.dart'; +import 'package:uuid/uuid.dart'; +import 'package:tallee/core/enums.dart'; + +class Game { + final String id; + final DateTime createdAt; + final String name; + final Ruleset ruleset; + final String description; + final GameColor color; + final String icon; + + Game({ + String? id, + DateTime? createdAt, + required this.name, + required this.ruleset, + String? description, + required this.color, + required this.icon, + }) : id = id ?? const Uuid().v4(), + createdAt = createdAt ?? clock.now(), + description = description ?? ''; + + @override + String toString() { + return 'Game{id: $id, name: $name, ruleset: $ruleset, description: $description, color: $color, icon: $icon}'; + } + + /// Creates a Game instance from a JSON object. + Game.fromJson(Map json) + : id = json['id'], + createdAt = DateTime.parse(json['createdAt']), + name = json['name'], + ruleset = Ruleset.values.firstWhere((e) => e.name == json['ruleset']), + description = json['description'], + color = GameColor.values.firstWhere((e) => e.name == json['color']), + icon = json['icon']; + + /// Converts the Game instance to a JSON object. + Map toJson() => { + 'id': id, + 'createdAt': createdAt.toIso8601String(), + 'name': name, + 'ruleset': ruleset.name, + 'description': description, + 'color': color.name, + 'icon': icon, + }; +} + diff --git a/lib/data/dto/group.dart b/lib/data/dto/group.dart index f02d98f..7676b1e 100644 --- a/lib/data/dto/group.dart +++ b/lib/data/dto/group.dart @@ -4,37 +4,41 @@ import 'package:uuid/uuid.dart'; class Group { final String id; - final DateTime createdAt; final String name; + final String description; + final DateTime createdAt; final List members; Group({ String? id, DateTime? createdAt, required this.name, + String? description, required this.members, }) : id = id ?? const Uuid().v4(), - createdAt = createdAt ?? clock.now(); + createdAt = createdAt ?? clock.now(), + description = description ?? ''; @override String toString() { - return 'Group{id: $id, name: $name,members: $members}'; + return 'Group{id: $id, name: $name, description: $description, members: $members}'; } - /// Creates a Group instance from a JSON object. + /// Creates a Group instance from a JSON object (memberIds format). + /// Player objects are reconstructed from memberIds by the DataTransferService. Group.fromJson(Map json) : id = json['id'], createdAt = DateTime.parse(json['createdAt']), name = json['name'], - members = (json['members'] as List) - .map((memberJson) => Player.fromJson(memberJson)) - .toList(); + description = json['description'], + members = []; // Populated during import via DataTransferService - /// Converts the Group instance to a JSON object. + /// Converts the Group instance to a JSON object using normalized format (memberIds only). Map toJson() => { 'id': id, 'createdAt': createdAt.toIso8601String(), 'name': name, - 'members': members.map((member) => member.toJson()).toList(), + 'description': description, + 'memberIds': members.map((member) => member.id).toList(), }; } diff --git a/lib/data/dto/match.dart b/lib/data/dto/match.dart index d3a8333..3976d36 100644 --- a/lib/data/dto/match.dart +++ b/lib/data/dto/match.dart @@ -1,4 +1,6 @@ import 'package:clock/clock.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/player.dart'; import 'package:uuid/uuid.dart'; @@ -6,46 +8,54 @@ import 'package:uuid/uuid.dart'; class Match { final String id; final DateTime createdAt; + final DateTime? endedAt; final String name; - final List? players; + final Game game; final Group? group; + final List players; + final String notes; Player? winner; Match({ String? id, DateTime? createdAt, + this.endedAt, required this.name, - this.players, + required this.game, this.group, + this.players = const [], + String? notes, this.winner, }) : id = id ?? const Uuid().v4(), - createdAt = createdAt ?? clock.now(); + createdAt = createdAt ?? clock.now(), + notes = notes ?? ''; @override String toString() { - return 'Match{\n\tid: $id,\n\tname: $name,\n\tplayers: $players,\n\tgroup: $group,\n\twinner: $winner\n}'; + return 'Match{id: $id, name: $name, game: $game, group: $group, players: $players, notes: $notes, endedAt: $endedAt}'; } - /// Creates a Match instance from a JSON object. + /// Creates a Match instance from a JSON object (ID references format). + /// Related objects are reconstructed from IDs by the DataTransferService. Match.fromJson(Map json) - : id = json['id'], - name = json['name'], - createdAt = DateTime.parse(json['createdAt']), - players = json['players'] != null - ? (json['players'] as List) - .map((playerJson) => Player.fromJson(playerJson)) - .toList() - : null, - group = json['group'] != null ? Group.fromJson(json['group']) : null, - winner = json['winner'] != null ? Player.fromJson(json['winner']) : null; + : id = json['id'], + createdAt = DateTime.parse(json['createdAt']), + endedAt = json['endedAt'] != null ? DateTime.parse(json['endedAt']) : null, + name = json['name'], + game = Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''), // Populated during import via DataTransferService + group = null, // Populated during import via DataTransferService + players = [], // Populated during import via DataTransferService + notes = json['notes'] ?? ''; - /// Converts the Match instance to a JSON object. + /// Converts the Match instance to a JSON object using normalized format (ID references only). Map toJson() => { 'id': id, 'createdAt': createdAt.toIso8601String(), + 'endedAt': endedAt?.toIso8601String(), 'name': name, - 'players': players?.map((player) => player.toJson()).toList(), - 'group': group?.toJson(), - 'winner': winner?.toJson(), + 'gameId': game.id, + 'groupId': group?.id, + 'playerIds': players.map((player) => player.id).toList(), + 'notes': notes, }; } diff --git a/lib/data/dto/player.dart b/lib/data/dto/player.dart index cfb4f4b..c405de9 100644 --- a/lib/data/dto/player.dart +++ b/lib/data/dto/player.dart @@ -5,26 +5,34 @@ class Player { final String id; final DateTime createdAt; final String name; + final String description; - Player({String? id, DateTime? createdAt, required this.name}) - : id = id ?? const Uuid().v4(), - createdAt = createdAt ?? clock.now(); + Player({ + String? id, + DateTime? createdAt, + required this.name, + String? description, + }) : id = id ?? const Uuid().v4(), + createdAt = createdAt ?? clock.now(), + description = description ?? ''; @override String toString() { - return 'Player{id: $id,name: $name}'; + return 'Player{id: $id, name: $name, description: $description}'; } /// Creates a Player instance from a JSON object. Player.fromJson(Map json) : id = json['id'], createdAt = DateTime.parse(json['createdAt']), - name = json['name']; + name = json['name'], + description = json['description']; /// Converts the Player instance to a JSON object. Map toJson() => { 'id': id, 'createdAt': createdAt.toIso8601String(), 'name': name, + 'description': description, }; } diff --git a/lib/data/dto/team.dart b/lib/data/dto/team.dart new file mode 100644 index 0000000..56036b2 --- /dev/null +++ b/lib/data/dto/team.dart @@ -0,0 +1,40 @@ +import 'package:clock/clock.dart'; +import 'package:tallee/data/dto/player.dart'; +import 'package:uuid/uuid.dart'; + +class Team { + final String id; + final String name; + final DateTime createdAt; + final List members; + + Team({ + String? id, + required this.name, + DateTime? createdAt, + required this.members, + }) : id = id ?? const Uuid().v4(), + createdAt = createdAt ?? clock.now(); + + @override + String toString() { + return 'Team{id: $id, name: $name, members: $members}'; + } + + /// Creates a Team instance from a JSON object (memberIds format). + /// Player objects are reconstructed from memberIds by the DataTransferService. + Team.fromJson(Map json) + : id = json['id'], + name = json['name'], + createdAt = DateTime.parse(json['createdAt']), + members = []; // Populated during import via DataTransferService + + /// Converts the Team instance to a JSON object using normalized format (memberIds only). + Map toJson() => { + 'id': id, + 'name': name, + 'createdAt': createdAt.toIso8601String(), + 'memberIds': members.map((member) => member.id).toList(), + }; +} + diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 74d541d..e0efc2c 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -82,6 +82,9 @@ "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", + "highest_score": "Höchste Punkte", + "lowest_score": "Niedrigste Punkte", + "multiple_winners": "Mehrere Gewinner:innen", "statistics": "Statistiken", "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 667c5c5..6a64a1b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -380,6 +380,9 @@ "settings": "Settings", "single_loser": "Single Loser", "single_winner": "Single Winner", + "highest_score": "Highest Score", + "lowest_score": "Lowest Score", + "multiple_winners": "Multiple Winners", "statistics": "Statistics", "stats": "Stats", "successfully_added_player": "Successfully added player {playerName}", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index b2a60c0..87fab99 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -590,6 +590,24 @@ abstract class AppLocalizations { /// **'Single Winner'** String get single_winner; + /// No description provided for @highest_score. + /// + /// In en, this message translates to: + /// **'Highest Score'** + String get highest_score; + + /// No description provided for @lowest_score. + /// + /// In en, this message translates to: + /// **'Lowest Score'** + String get lowest_score; + + /// No description provided for @multiple_winners. + /// + /// In en, this message translates to: + /// **'Multiple Winners'** + String get multiple_winners; + /// Statistics tab label /// /// In en, this message translates to: diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart index 2f76fd2..54f3bc7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -266,6 +266,15 @@ class AppLocalizationsDe extends AppLocalizations { @override String get single_winner => 'Ein:e Gewinner:in'; + @override + String get highest_score => 'Höchste Punkte'; + + @override + String get lowest_score => 'Niedrigste Punkte'; + + @override + String get multiple_winners => 'Mehrere Gewinner:innen'; + @override String get statistics => 'Statistiken'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index cfcae20..440ac10 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -266,6 +266,15 @@ class AppLocalizationsEn extends AppLocalizations { @override String get single_winner => 'Single Winner'; + @override + String get highest_score => 'Highest Score'; + + @override + String get lowest_score => 'Lowest Score'; + + @override + String get multiple_winners => 'Multiple Winners'; + @override String get statistics => 'Statistics'; diff --git a/lib/main.dart b/lib/main.dart index 0002531..59384ac 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,9 +29,7 @@ class GameTracker extends StatelessWidget { return supportedLocale; } } - return supportedLocales.firstWhere( - (locale) => locale.languageCode == 'en', - ); + return supportedLocales.firstWhere((locale) => locale.languageCode == 'en'); }, debugShowCheckedModeBanner: false, onGenerateTitle: (context) => AppLocalizations.of(context).app_name, diff --git a/lib/presentation/views/main_menu/group_view/create_group_view.dart b/lib/presentation/views/main_menu/group_view/create_group_view.dart index 2b7ab86..100023a 100644 --- a/lib/presentation/views/main_menu/group_view/create_group_view.dart +++ b/lib/presentation/views/main_menu/group_view/create_group_view.dart @@ -84,6 +84,7 @@ class _CreateGroupViewState extends State { bool success = await db.groupDao.addGroup( group: Group( name: _groupNameController.text.trim(), + description: '', members: selectedPlayers, ), ); diff --git a/lib/presentation/views/main_menu/group_view/groups_view.dart b/lib/presentation/views/main_menu/group_view/groups_view.dart index 6035fc8..dd47e12 100644 --- a/lib/presentation/views/main_menu/group_view/groups_view.dart +++ b/lib/presentation/views/main_menu/group_view/groups_view.dart @@ -35,7 +35,8 @@ class _GroupsViewState extends State { 7, Group( name: 'Skeleton Group', - members: List.filled(6, Player(name: 'Skeleton Player')), + description: '', + members: List.filled(6, Player(name: 'Skeleton Player', description: '')), ), ); diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index 63617b2..a7f5cfa 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -2,7 +2,9 @@ import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/constants.dart'; +import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; @@ -40,13 +42,16 @@ class _HomeViewState extends State { 2, Match( name: 'Skeleton Match', + game: Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''), group: Group( name: 'Skeleton Group', + description: '', members: [ - Player(name: 'Skeleton Player 1'), - Player(name: 'Skeleton Player 2'), + Player(name: 'Skeleton Player 1', description: ''), + Player(name: 'Skeleton Player 2', description: ''), ], ), + notes: '', ), ); @@ -99,9 +104,7 @@ class _HomeViewState extends State { if (recentMatches.isNotEmpty) for (Match match in recentMatches) Padding( - padding: const EdgeInsets.symmetric( - vertical: 6.0, - ), + padding: const EdgeInsets.symmetric(vertical: 6.0), child: MatchTile( compact: true, width: constraints.maxWidth * 0.9, @@ -110,19 +113,15 @@ class _HomeViewState extends State { await Navigator.of(context).push( adaptivePageRoute( fullscreenDialog: true, - builder: (context) => - MatchResultView(match: match), + builder: (context) => MatchResultView(match: match), ), ); - await updatedWinnerinRecentMatches(match.id); + await updatedWinnerInRecentMatches(match.id); }, ), ) else - Center( - heightFactor: 5, - child: Text(loc.no_recent_matches_available), - ), + Center(heightFactor: 5, child: Text(loc.no_recent_matches_available)), ], ), ), @@ -138,40 +137,22 @@ class _HomeViewState extends State { Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - QuickCreateButton( - text: 'Category 1', - onPressed: () {}, - ), - QuickCreateButton( - text: 'Category 2', - onPressed: () {}, - ), + QuickCreateButton(text: 'Category 1', onPressed: () {}), + QuickCreateButton(text: 'Category 2', onPressed: () {}), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - QuickCreateButton( - text: 'Category 3', - onPressed: () {}, - ), - QuickCreateButton( - text: 'Category 4', - onPressed: () {}, - ), + QuickCreateButton(text: 'Category 3', onPressed: () {}), + QuickCreateButton(text: 'Category 4', onPressed: () {}), ], ), Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ - QuickCreateButton( - text: 'Category 5', - onPressed: () {}, - ), - QuickCreateButton( - text: 'Category 6', - onPressed: () {}, - ), + QuickCreateButton(text: 'Category 5', onPressed: () {}), + QuickCreateButton(text: 'Category 6', onPressed: () {}), ], ), ], @@ -200,11 +181,9 @@ class _HomeViewState extends State { matchCount = results[0] as int; groupCount = results[1] as int; loadedRecentMatches = results[2] as List; - recentMatches = - (loadedRecentMatches - ..sort((a, b) => b.createdAt.compareTo(a.createdAt))) - .take(2) - .toList(); + recentMatches = (loadedRecentMatches..sort((a, b) => b.createdAt.compareTo(a.createdAt))) + .take(2) + .toList(); if (mounted) { setState(() { isLoading = false; @@ -214,7 +193,7 @@ class _HomeViewState extends State { } /// Updates the winner information for a specific match in the recent matches list. - Future updatedWinnerinRecentMatches(String matchId) async { + Future updatedWinnerInRecentMatches(String matchId) async { final db = Provider.of(context, listen: false); final winner = await db.matchDao.getWinner(matchId: matchId); final matchIndex = recentMatches.indexWhere((match) => match.id == matchId); diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart index 8182ddb..7096702 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart @@ -5,6 +5,7 @@ import 'package:tallee/core/constants.dart'; import 'package:tallee/core/custom_theme.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; @@ -62,7 +63,7 @@ class _CreateMatchViewState extends State { int selectedGameIndex = -1; /// The currently selected players - List? selectedPlayers; + List selectedPlayers = []; @override void initState() { @@ -99,7 +100,7 @@ class _CreateMatchViewState extends State { } List<(String, String, Ruleset)> games = [ - ('Example Game 1', 'This is a description', Ruleset.leastPoints), + ('Example Game 1', 'This is a description', Ruleset.lowestScore), ('Example Game 2', '', Ruleset.singleWinner), ]; @@ -165,8 +166,8 @@ class _CreateMatchViewState extends State { filteredPlayerList = playerList .where( (p) => - !selectedGroup!.members.any((m) => m.id == p.id), - ) + !selectedGroup!.members.any((m) => m.id == p.id), + ) .toList(); } else { filteredPlayerList = List.from(playerList); @@ -177,7 +178,7 @@ class _CreateMatchViewState extends State { Expanded( child: PlayerSelection( key: ValueKey(selectedGroup?.id ?? 'no_group'), - initialSelectedPlayers: selectedPlayers ?? [], + initialSelectedPlayers: selectedPlayers, availablePlayers: filteredPlayerList, onChanged: (value) { setState(() { @@ -192,28 +193,56 @@ class _CreateMatchViewState extends State { buttonType: ButtonType.primary, onPressed: _enableCreateGameButton() ? () async { - Match match = Match( - name: _matchNameController.text.isEmpty - ? (hintText ?? '') - : _matchNameController.text.trim(), - createdAt: DateTime.now(), - group: selectedGroup, - players: selectedPlayers, - ); - await db.matchDao.addMatch(match: match); - if (context.mounted) { - Navigator.pushReplacement( - context, - adaptivePageRoute( - fullscreenDialog: true, - builder: (context) => MatchResultView( - match: match, - onWinnerChanged: widget.onWinnerChanged, - ), - ), - ); - } - } + // Use a game from the games list + Game? gameToUse; + if (selectedGameIndex == -1) { + // Use the first game as default if none selected + final selectedGame = games[0]; + gameToUse = Game( + name: selectedGame.$1, + description: selectedGame.$2, + ruleset: selectedGame.$3, + color: GameColor.blue, + icon: '', + ); + } else { + // Use the selected game from the list + final selectedGame = games[selectedGameIndex]; + gameToUse = Game( + name: selectedGame.$1, + description: selectedGame.$2, + ruleset: selectedGame.$3, + color: GameColor.blue, + icon: '', + ); + } + // Add the game to the database if it doesn't exist + await db.gameDao.addGame(game: gameToUse); + + Match match = Match( + name: _matchNameController.text.isEmpty + ? (hintText ?? '') + : _matchNameController.text.trim(), + createdAt: DateTime.now(), + game: gameToUse, + group: selectedGroup, + players: selectedPlayers, + notes: '', + ); + await db.matchDao.addMatch(match: match); + if (context.mounted) { + Navigator.pushReplacement( + context, + adaptivePageRoute( + fullscreenDialog: true, + builder: (context) => MatchResultView( + match: match, + onWinnerChanged: widget.onWinnerChanged, + ), + ), + ); + } + } : null, ), ], @@ -230,6 +259,6 @@ class _CreateMatchViewState extends State { /// - Either a group is selected OR at least 2 players are selected bool _enableCreateGameButton() { return (selectedGroup != null || - (selectedPlayers != null && selectedPlayers!.length > 1)); + (selectedPlayers.length > 1)); } -} +} \ No newline at end of file diff --git a/lib/presentation/views/main_menu/match_view/match_result_view.dart b/lib/presentation/views/main_menu/match_view/match_result_view.dart index 214c500..4f3f0c0 100644 --- a/lib/presentation/views/main_menu/match_view/match_result_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_result_view.dart @@ -153,12 +153,10 @@ class _MatchResultViewState extends State { List getAllPlayers(Match match) { List players = []; - if (match.group == null && match.players != null) { - players = [...match.players!]; - } else if (match.group != null && match.players != null) { - players = [...match.players!, ...match.group!.members]; + if (match.group == null) { + players = [...match.players]; } else { - players = [...match.group!.members]; + players = [...match.players, ...match.group!.members]; } players.sort((a, b) => a.name.compareTo(b.name)); diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index c34a22f..1f50342 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -6,7 +6,9 @@ import 'package:provider/provider.dart'; import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/constants.dart'; import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; @@ -36,12 +38,15 @@ class _MatchViewState extends State { 4, Match( name: 'Skeleton match name', + game: Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''), group: Group( name: 'Group name', - members: List.filled(5, Player(name: 'Player')), + description: '', + members: List.filled(5, Player(name: 'Player', description: '')), ), - winner: Player(name: 'Player'), - players: [Player(name: 'Player')], + winner: Player(name: 'Player', description: ''), + players: [Player(name: 'Player', description: '')], + notes: '', ), ); diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view.dart index e552e54..5b3ff22 100644 --- a/lib/presentation/views/main_menu/statistics_view.dart +++ b/lib/presentation/views/main_menu/statistics_view.dart @@ -167,7 +167,7 @@ class _StatisticsViewState extends State { final playerId = winCounts[i].$1; final player = players.firstWhere( (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), + orElse: () => Player(id: playerId, name: loc.not_available, description: ''), ); winCounts[i] = (player.name, winCounts[i].$2); } @@ -202,19 +202,17 @@ class _StatisticsViewState extends State { } } } - if (match.players != null) { - final members = match.players!.map((p) => p.id).toList(); - for (var playerId in members) { - final index = matchCounts.indexWhere((entry) => entry.$1 == playerId); - // -1 means player not found in matchCounts - if (index != -1) { - final current = matchCounts[index].$2; + final members = match.players.map((p) => p.id).toList(); + for (var playerId in members) { + final index = matchCounts.indexWhere((entry) => entry.$1 == playerId); + // -1 means player not found in matchCounts + if (index != -1) { + final current = matchCounts[index].$2; matchCounts[index] = (playerId, current + 1); } else { matchCounts.add((playerId, 1)); } } - } } // Adding all players with zero matches @@ -231,7 +229,7 @@ class _StatisticsViewState extends State { final playerId = matchCounts[i].$1; final player = players.firstWhere( (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), + orElse: () => Player(id: playerId, name: loc.not_available, description: ''), ); matchCounts[i] = (player.name, matchCounts[i].$2); } diff --git a/lib/presentation/widgets/player_selection.dart b/lib/presentation/widgets/player_selection.dart index bb3e3b9..6e16c61 100644 --- a/lib/presentation/widgets/player_selection.dart +++ b/lib/presentation/widgets/player_selection.dart @@ -62,7 +62,7 @@ class _PlayerSelectionState extends State { /// Skeleton data used while loading players. late final List skeletonData = List.filled( 7, - Player(name: 'Player 0'), + Player(name: 'Player 0', description: ''), ); @override @@ -260,7 +260,7 @@ class _PlayerSelectionState extends State { final loc = AppLocalizations.of(context); final playerName = _searchBarController.text.trim(); - final createdPlayer = Player(name: playerName); + final createdPlayer = Player(name: playerName, description: ''); final success = await db.playerDao.addPlayer(player: createdPlayer); if (!context.mounted) return; diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index ab65e5d..7862000 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -91,7 +91,7 @@ class _MatchTileState extends State { const SizedBox(width: 6), Expanded( child: Text( - '${group.name}${widget.match.players != null ? ' + ${widget.match.players?.length}' : ''}', + '${group.name} + ${widget.match.players.length}', style: const TextStyle(fontSize: 14, color: Colors.grey), overflow: TextOverflow.ellipsis, ), @@ -106,7 +106,7 @@ class _MatchTileState extends State { const SizedBox(width: 6), Expanded( child: Text( - '${widget.match.players!.length} ${loc.players}', + '${widget.match.players.length} ${loc.players}', style: const TextStyle(fontSize: 14, color: Colors.grey), overflow: TextOverflow.ellipsis, ), @@ -241,12 +241,10 @@ class _MatchTileState extends State { final playerIds = {}; // Add players from game.players - if (widget.match.players != null) { - for (var player in widget.match.players!) { - if (!playerIds.contains(player.id)) { - allPlayers.add(player); - playerIds.add(player.id); - } + for (var player in widget.match.players) { + if (!playerIds.contains(player.id)) { + allPlayers.add(player); + playerIds.add(player.id); } } diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 5863d87..526a459 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -8,53 +8,65 @@ import 'package:json_schema/json_schema.dart'; import 'package:provider/provider.dart'; import 'package:tallee/core/enums.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/data/dto/team.dart'; class DataTransferService { /// Deletes all data from the database. static Future deleteAllData(BuildContext context) async { final db = Provider.of(context, listen: false); await db.matchDao.deleteAllMatches(); + await db.teamDao.deleteAllTeams(); await db.groupDao.deleteAllGroups(); + await db.gameDao.deleteAllGames(); await db.playerDao.deleteAllPlayers(); } /// Retrieves all application data and converts it to a JSON string. - /// Returns the JSON string representation of the data. + /// Returns the JSON string representation of the data in normalized format. static Future getAppDataAsJson(BuildContext context) async { final db = Provider.of(context, listen: false); final matches = await db.matchDao.getAllMatches(); final groups = await db.groupDao.getAllGroups(); final players = await db.playerDao.getAllPlayers(); + final games = await db.gameDao.getAllGames(); + final teams = await db.teamDao.getAllTeams(); - // Construct a JSON representation of the data + // Construct a JSON representation of the data in normalized format final Map jsonMap = { 'players': players.map((p) => p.toJson()).toList(), - + 'games': games.map((g) => g.toJson()).toList(), 'groups': groups - .map( - (g) => { - 'id': g.id, - 'name': g.name, - 'createdAt': g.createdAt.toIso8601String(), - 'memberIds': (g.members).map((m) => m.id).toList(), - }, - ) + .map((g) => { + 'id': g.id, + 'name': g.name, + 'description': g.description, + 'createdAt': g.createdAt.toIso8601String(), + 'memberIds': (g.members).map((m) => m.id).toList(), + }) + .toList(), + 'teams': teams + .map((t) => { + 'id': t.id, + 'name': t.name, + 'createdAt': t.createdAt.toIso8601String(), + 'memberIds': (t.members).map((m) => m.id).toList(), + }) .toList(), - 'matches': matches - .map( - (m) => { - 'id': m.id, - 'name': m.name, - 'createdAt': m.createdAt.toIso8601String(), - 'groupId': m.group?.id, - 'playerIds': (m.players ?? []).map((p) => p.id).toList(), - 'winnerId': m.winner?.id, - }, - ) + .map((m) => { + 'id': m.id, + 'name': m.name, + 'createdAt': m.createdAt.toIso8601String(), + 'endedAt': m.endedAt?.toIso8601String(), + 'gameId': m.game.id, + 'groupId': m.group?.id, + 'playerIds': m.players.map((p) => p.id).toList(), + 'notes': m.notes, + }) .toList(), }; @@ -67,9 +79,9 @@ class DataTransferService { /// [jsonString] The JSON string to be exported. /// [fileName] The desired name for the exported file (without extension). static Future exportData( - String jsonString, - String fileName, - ) async { + String jsonString, + String fileName + ) async { try { final bytes = Uint8List.fromList(utf8.encode(jsonString)); final path = await FilePicker.platform.saveFile( @@ -82,6 +94,7 @@ class DataTransferService { } else { return ExportResult.success; } + } catch (e, stack) { print('[exportData] $e'); print(stack); @@ -109,17 +122,15 @@ class DataTransferService { final isValid = await _validateJsonSchema(jsonString); if (!isValid) return ImportResult.invalidSchema; - final Map decoded = - json.decode(jsonString) as Map; + final Map decoded = json.decode(jsonString) as Map; - final List playersJson = - (decoded['players'] as List?) ?? []; - final List groupsJson = - (decoded['groups'] as List?) ?? []; - final List matchesJson = - (decoded['matches'] as List?) ?? []; + final List playersJson = (decoded['players'] as List?) ?? []; + final List gamesJson = (decoded['games'] as List?) ?? []; + final List groupsJson = (decoded['groups'] as List?) ?? []; + final List teamsJson = (decoded['teams'] as List?) ?? []; + final List matchesJson = (decoded['matches'] as List?) ?? []; - // Players + // Import Players final List importedPlayers = playersJson .map((p) => Player.fromJson(p as Map)) .toList(); @@ -128,11 +139,19 @@ class DataTransferService { for (final p in importedPlayers) p.id: p, }; - // Groups + // Import Games + final List importedGames = gamesJson + .map((g) => Game.fromJson(g as Map)) + .toList(); + + final Map gameById = { + for (final g in importedGames) g.id: g, + }; + + // Import Groups final List importedGroups = groupsJson.map((g) { final map = g as Map; - final memberIds = (map['memberIds'] as List? ?? []) - .cast(); + final memberIds = (map['memberIds'] as List? ?? []).cast(); final members = memberIds .map((id) => playerById[id]) @@ -142,6 +161,7 @@ class DataTransferService { return Group( id: map['id'] as String, name: map['name'] as String, + description: map['description'] as String, members: members, createdAt: DateTime.parse(map['createdAt'] as String), ); @@ -151,34 +171,57 @@ class DataTransferService { for (final g in importedGroups) g.id: g, }; - // Matches + // Import Teams + final List importedTeams = teamsJson.map((t) { + final map = t as Map; + final memberIds = (map['memberIds'] as List? ?? []).cast(); + + final members = memberIds + .map((id) => playerById[id]) + .whereType() + .toList(); + + return Team( + id: map['id'] as String, + name: map['name'] as String, + members: members, + createdAt: DateTime.parse(map['createdAt'] as String), + ); + }).toList(); + + // Import Matches final List importedMatches = matchesJson.map((m) { final map = m as Map; + final String gameId = map['gameId'] as String; final String? groupId = map['groupId'] as String?; - final List playerIds = - (map['playerIds'] as List? ?? []).cast(); - final String? winnerId = map['winnerId'] as String?; + final List playerIds = (map['playerIds'] as List? ?? []).cast(); + final DateTime? endedAt = map['endedAt'] != null ? DateTime.parse(map['endedAt'] as String) : null; + final game = gameById[gameId]; final group = (groupId == null) ? null : groupById[groupId]; final players = playerIds .map((id) => playerById[id]) .whereType() .toList(); - final winner = (winnerId == null) ? null : playerById[winnerId]; return Match( id: map['id'] as String, name: map['name'] as String, + game: game ?? Game(name: 'Unknown', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''), group: group, players: players, createdAt: DateTime.parse(map['createdAt'] as String), - winner: winner, + endedAt: endedAt, + notes: map['notes'] as String? ?? '', ); }).toList(); + // Import all data into the database await db.playerDao.addPlayersAsList(players: importedPlayers); + await db.gameDao.addGamesAsList(games: importedGames); await db.groupDao.addGroupsAsList(groups: importedGroups); + await db.teamDao.addTeamsAsList(teams: importedTeams); await db.matchDao.addMatchAsList(matches: importedMatches); return ImportResult.success; @@ -223,4 +266,4 @@ class DataTransferService { return false; } } -} +} \ No newline at end of file diff --git a/test/db_tests/aggregates/group_test.dart b/test/db_tests/aggregates/group_test.dart new file mode 100644 index 0000000..a671232 --- /dev/null +++ b/test/db_tests/aggregates/group_test.dart @@ -0,0 +1,374 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/group.dart'; +import 'package:tallee/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Group testGroup1; + late Group testGroup2; + late Group testGroup3; + late Group testGroup4; + final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + testGroup1 = Group( + name: 'Test Group', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testGroup2 = Group( + id: 'gr2', + name: 'Second Group', + description: '', + members: [testPlayer2, testPlayer3, testPlayer4], + ); + testGroup3 = Group( + id: 'gr2', + name: 'Second Group', + description: '', + members: [testPlayer2, testPlayer4], + ); + testGroup4 = Group( + id: 'gr2', + name: 'Second Group', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], + ); + }); + }); + tearDown(() async { + await database.close(); + }); + group('Group Tests', () { + + // Verifies that a single group can be added and retrieved with all fields and members intact. + test('Adding and fetching a single group works correctly', () async { + await database.groupDao.addGroup(group: testGroup1); + + final fetchedGroup = await database.groupDao.getGroupById( + groupId: testGroup1.id, + ); + + expect(fetchedGroup.id, testGroup1.id); + expect(fetchedGroup.name, testGroup1.name); + expect(fetchedGroup.createdAt, testGroup1.createdAt); + + expect(fetchedGroup.members.length, testGroup1.members.length); + for (int i = 0; i < testGroup1.members.length; i++) { + expect(fetchedGroup.members[i].id, testGroup1.members[i].id); + expect(fetchedGroup.members[i].name, testGroup1.members[i].name); + expect( + fetchedGroup.members[i].createdAt, + testGroup1.members[i].createdAt, + ); + } + }); + + // Verifies that multiple groups can be added and retrieved with correct members. + test('Adding and fetching multiple groups works correctly', () async { + await database.groupDao.addGroupsAsList( + groups: [testGroup1, testGroup2, testGroup3, testGroup4], + ); + + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups.length, 2); + + final testGroups = {testGroup1.id: testGroup1, testGroup2.id: testGroup2}; + + for (final group in allGroups) { + final testGroup = testGroups[group.id]!; + + expect(group.id, testGroup.id); + expect(group.name, testGroup.name); + expect(group.createdAt, testGroup.createdAt); + + expect(group.members.length, testGroup.members.length); + for (int i = 0; i < testGroup.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); + } + } + }); + + // Verifies that adding the same group twice does not create duplicates. + test('Adding the same group twice does not create duplicates', () async { + await database.groupDao.addGroup(group: testGroup1); + await database.groupDao.addGroup(group: testGroup1); + + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups.length, 1); + }); + + // Verifies that groupExists returns correct boolean based on group presence. + test('Group existence check works correctly', () async { + var groupExists = await database.groupDao.groupExists( + groupId: testGroup1.id, + ); + expect(groupExists, false); + + await database.groupDao.addGroup(group: testGroup1); + + groupExists = await database.groupDao.groupExists(groupId: testGroup1.id); + expect(groupExists, true); + }); + + // Verifies that deleteGroup removes the group and returns true. + test('Deleting a group works correctly', () async { + await database.groupDao.addGroup(group: testGroup1); + + final groupDeleted = await database.groupDao.deleteGroup( + groupId: testGroup1.id, + ); + expect(groupDeleted, true); + + final groupExists = await database.groupDao.groupExists( + groupId: testGroup1.id, + ); + expect(groupExists, false); + }); + + // Verifies that updateGroupName correctly updates only the name field. + test('Updating a group name works correctly', () async { + await database.groupDao.addGroup(group: testGroup1); + + const newGroupName = 'new group name'; + + await database.groupDao.updateGroupName( + groupId: testGroup1.id, + newName: newGroupName, + ); + + final result = await database.groupDao.getGroupById( + groupId: testGroup1.id, + ); + expect(result.name, newGroupName); + }); + + // Verifies that getGroupCount returns correct count through add/delete operations. + test('Getting the group count works correctly', () async { + final initialCount = await database.groupDao.getGroupCount(); + expect(initialCount, 0); + + await database.groupDao.addGroup(group: testGroup1); + + final groupAdded = await database.groupDao.getGroupCount(); + expect(groupAdded, 1); + + final groupRemoved = await database.groupDao.deleteGroup( + groupId: testGroup1.id, + ); + expect(groupRemoved, true); + + final finalCount = await database.groupDao.getGroupCount(); + expect(finalCount, 0); + }); + + // Verifies that getAllGroups returns an empty list when no groups exist. + test('getAllGroups returns empty list when no groups exist', () async { + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups, isEmpty); + }); + + // Verifies that getGroupById throws StateError for non-existent group ID. + test('getGroupById throws exception for non-existent group', () async { + expect( + () => database.groupDao.getGroupById(groupId: 'non-existent-id'), + throwsA(isA()), + ); + }); + + // Verifies that addGroup returns false when trying to add a duplicate group. + test('addGroup returns false when group already exists', () async { + final firstAdd = await database.groupDao.addGroup(group: testGroup1); + expect(firstAdd, true); + + final secondAdd = await database.groupDao.addGroup(group: testGroup1); + expect(secondAdd, false); + + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups.length, 1); + }); + + // Verifies that addGroupsAsList handles an empty list without errors. + test('addGroupsAsList handles empty list correctly', () async { + await database.groupDao.addGroupsAsList(groups: []); + + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups.length, 0); + }); + + // Verifies that deleteGroup returns false for a non-existent group ID. + test('deleteGroup returns false for non-existent group', () async { + final deleted = await database.groupDao.deleteGroup( + groupId: 'non-existent-id', + ); + expect(deleted, false); + }); + + // Verifies that updateGroupName returns false for a non-existent group ID. + test('updateGroupName returns false for non-existent group', () async { + final updated = await database.groupDao.updateGroupName( + groupId: 'non-existent-id', + newName: 'New Name', + ); + expect(updated, false); + }); + + // Verifies that updateGroupDescription correctly updates the description field. + test('Updating a group description works correctly', () async { + await database.groupDao.addGroup(group: testGroup1); + + const newDescription = 'This is a new description'; + + final updated = await database.groupDao.updateGroupDescription( + groupId: testGroup1.id, + newDescription: newDescription, + ); + expect(updated, true); + + final result = await database.groupDao.getGroupById( + groupId: testGroup1.id, + ); + expect(result.description, newDescription); + }); + + // Verifies that updateGroupDescription can set the description to null. + test('updateGroupDescription can set description to null', () async { + final groupWithDescription = Group( + name: 'Group with description', + description: 'Initial description', + members: [testPlayer1], + ); + await database.groupDao.addGroup(group: groupWithDescription); + + final updated = await database.groupDao.updateGroupDescription( + groupId: groupWithDescription.id, + newDescription: 'Updated description', + ); + expect(updated, true); + + final result = await database.groupDao.getGroupById( + groupId: groupWithDescription.id, + ); + expect(result.description, 'Updated description'); + }); + + // Verifies that updateGroupDescription returns false for a non-existent group. + test('updateGroupDescription returns false for non-existent group', + () async { + final updated = await database.groupDao.updateGroupDescription( + groupId: 'non-existent-id', + newDescription: 'New Description', + ); + expect(updated, false); + }); + + // Verifies that deleteAllGroups removes all groups from the database. + test('deleteAllGroups removes all groups', () async { + await database.groupDao.addGroupsAsList( + groups: [testGroup1, testGroup2], + ); + + final countBefore = await database.groupDao.getGroupCount(); + expect(countBefore, 2); + + final deleted = await database.groupDao.deleteAllGroups(); + expect(deleted, true); + + final countAfter = await database.groupDao.getGroupCount(); + expect(countAfter, 0); + }); + + // Verifies that deleteAllGroups returns false when no groups exist. + test('deleteAllGroups returns false when no groups exist', () async { + final deleted = await database.groupDao.deleteAllGroups(); + expect(deleted, false); + }); + + // Verifies that groups with special characters (quotes, emojis) are stored correctly. + test('Group with special characters in name is stored correctly', () async { + final specialGroup = Group( + name: 'Group\'s & "Special" ', + description: 'Description with émojis 🎮🎲', + members: [testPlayer1], + ); + await database.groupDao.addGroup(group: specialGroup); + + final fetchedGroup = await database.groupDao.getGroupById( + groupId: specialGroup.id, + ); + expect(fetchedGroup.name, 'Group\'s & "Special" '); + expect(fetchedGroup.description, 'Description with émojis 🎮🎲'); + }); + + // Verifies that a group with an empty members list can be stored and retrieved. + test('Group with empty members list is stored correctly', () async { + final emptyGroup = Group( + name: 'Empty Group', + description: '', + members: [], + ); + await database.groupDao.addGroup(group: emptyGroup); + + final fetchedGroup = await database.groupDao.getGroupById( + groupId: emptyGroup.id, + ); + expect(fetchedGroup.name, 'Empty Group'); + expect(fetchedGroup.members, isEmpty); + }); + + // Verifies that multiple sequential updates to the same group work correctly. + test('Multiple updates to the same group work correctly', () async { + await database.groupDao.addGroup(group: testGroup1); + + await database.groupDao.updateGroupName( + groupId: testGroup1.id, + newName: 'Updated Name', + ); + await database.groupDao.updateGroupDescription( + groupId: testGroup1.id, + newDescription: 'Updated Description', + ); + + final updatedGroup = await database.groupDao.getGroupById( + groupId: testGroup1.id, + ); + expect(updatedGroup.name, 'Updated Name'); + expect(updatedGroup.description, 'Updated Description'); + expect(updatedGroup.members.length, testGroup1.members.length); + }); + + // Verifies that addGroupsAsList with duplicate groups only adds unique ones. + test('addGroupsAsList with duplicate groups only adds once', () async { + await database.groupDao.addGroupsAsList( + groups: [testGroup1, testGroup1, testGroup1], + ); + + final allGroups = await database.groupDao.getAllGroups(); + expect(allGroups.length, 1); + }); + }); +} diff --git a/test/db_tests/game_test.dart b/test/db_tests/aggregates/match_test.dart similarity index 62% rename from test/db_tests/game_test.dart rename to test/db_tests/aggregates/match_test.dart index 5a6ad25..0718e0d 100644 --- a/test/db_tests/game_test.dart +++ b/test/db_tests/aggregates/match_test.dart @@ -3,9 +3,11 @@ import 'package:drift/drift.dart'; import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; import 'package:tallee/data/dto/group.dart'; import 'package:tallee/data/dto/match.dart'; import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/core/enums.dart'; void main() { late AppDatabase database; @@ -16,6 +18,7 @@ void main() { late Player testPlayer5; late Group testGroup1; late Group testGroup2; + late Game testGame; late Match testMatch1; late Match testMatch2; late Match testMatchOnlyPlayers; @@ -33,39 +36,50 @@ void main() { ); withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testPlayer5 = Player(name: 'Eve'); + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + testPlayer5 = Player(name: 'Eve', description: ''); testGroup1 = Group( name: 'Test Group 2', + description: '', members: [testPlayer1, testPlayer2, testPlayer3], ); testGroup2 = Group( name: 'Test Group 2', + description: '', members: [testPlayer4, testPlayer5], ); + testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: GameColor.blue, icon: ''); testMatch1 = Match( name: 'First Test Match', + game: testGame, group: testGroup1, players: [testPlayer4, testPlayer5], winner: testPlayer4, + notes: '', ); testMatch2 = Match( name: 'Second Test Match', + game: testGame, group: testGroup2, players: [testPlayer1, testPlayer2, testPlayer3], winner: testPlayer2, + notes: '', ); testMatchOnlyPlayers = Match( name: 'Test Match with Players', + game: testGame, players: [testPlayer1, testPlayer2, testPlayer3], winner: testPlayer3, + notes: '', ); testMatchOnlyGroup = Match( name: 'Test Match with Group', + game: testGame, group: testGroup2, + notes: '', ); }); await database.playerDao.addPlayersAsList( @@ -78,12 +92,15 @@ void main() { ], ); await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); + await database.gameDao.addGame(game: testGame); }); tearDown(() async { await database.close(); }); group('Match Tests', () { + + // Verifies that a single match can be added and retrieved with all fields, group, and players intact. test('Adding and fetching single match works correctly', () async { await database.matchDao.addMatch(match: testMatch1); @@ -95,14 +112,6 @@ void main() { expect(result.name, testMatch1.name); expect(result.createdAt, testMatch1.createdAt); - if (result.winner != null && testMatch1.winner != null) { - expect(result.winner!.id, testMatch1.winner!.id); - expect(result.winner!.name, testMatch1.winner!.name); - expect(result.winner!.createdAt, testMatch1.winner!.createdAt); - } else { - expect(result.winner, testMatch1.winner); - } - if (result.group != null) { expect(result.group!.members.length, testGroup1.members.length); @@ -113,22 +122,19 @@ void main() { } else { fail('Group is null'); } - if (result.players != null) { - expect(result.players!.length, testMatch1.players!.length); + expect(result.players.length, testMatch1.players.length); - for (int i = 0; i < testMatch1.players!.length; i++) { - expect(result.players![i].id, testMatch1.players![i].id); - expect(result.players![i].name, testMatch1.players![i].name); - expect( - result.players![i].createdAt, - testMatch1.players![i].createdAt, - ); - } - } else { - fail('Players is null'); + for (int i = 0; i < testMatch1.players.length; i++) { + expect(result.players[i].id, testMatch1.players[i].id); + expect(result.players[i].name, testMatch1.players[i].name); + expect( + result.players[i].createdAt, + testMatch1.players[i].createdAt, + ); } }); + // Verifies that multiple matches can be added and retrieved with correct groups and players. test('Adding and fetching multiple matches works correctly', () async { await database.matchDao.addMatchAsList( matches: [ @@ -156,13 +162,6 @@ void main() { expect(match.id, testMatch.id); expect(match.name, testMatch.name); expect(match.createdAt, testMatch.createdAt); - if (match.winner != null && testMatch.winner != null) { - expect(match.winner!.id, testMatch.winner!.id); - expect(match.winner!.name, testMatch.winner!.name); - expect(match.winner!.createdAt, testMatch.winner!.createdAt); - } else { - expect(match.winner, testMatch.winner); - } // Group-Checks if (testMatch.group != null) { @@ -188,22 +187,19 @@ void main() { } // Players-Checks - if (testMatch.players != null) { - expect(match.players!.length, testMatch.players!.length); - for (int i = 0; i < testMatch.players!.length; i++) { - expect(match.players![i].id, testMatch.players![i].id); - expect(match.players![i].name, testMatch.players![i].name); - expect( - match.players![i].createdAt, - testMatch.players![i].createdAt, - ); - } - } else { - expect(match.players, null); + expect(match.players.length, testMatch.players.length); + for (int i = 0; i < testMatch.players.length; i++) { + expect(match.players[i].id, testMatch.players[i].id); + expect(match.players[i].name, testMatch.players[i].name); + expect( + match.players[i].createdAt, + testMatch.players[i].createdAt, + ); } } }); + // Verifies that adding the same match twice does not create duplicates. test('Adding the same match twice does not create duplicates', () async { await database.matchDao.addMatch(match: testMatch1); await database.matchDao.addMatch(match: testMatch1); @@ -212,6 +208,7 @@ void main() { expect(matchCount, 1); }); + // Verifies that matchExists returns correct boolean based on match presence. test('Match existence check works correctly', () async { var matchExists = await database.matchDao.matchExists( matchId: testMatch1.id, @@ -224,6 +221,7 @@ void main() { expect(matchExists, true); }); + // Verifies that deleteMatch removes the match and returns true. test('Deleting a match works correctly', () async { await database.matchDao.addMatch(match: testMatch1); @@ -238,6 +236,7 @@ void main() { expect(matchExists, false); }); + // Verifies that getMatchCount returns correct count through add/delete operations. test('Getting the match count works correctly', () async { var matchCount = await database.matchDao.getMatchCount(); expect(matchCount, 0); @@ -263,82 +262,7 @@ void main() { expect(matchCount, 0); }); - test('Checking if match has winner works correctly', () async { - await database.matchDao.addMatch(match: testMatch1); - await database.matchDao.addMatch(match: testMatchOnlyGroup); - - var hasWinner = await database.matchDao.hasWinner(matchId: testMatch1.id); - expect(hasWinner, true); - - hasWinner = await database.matchDao.hasWinner( - matchId: testMatchOnlyGroup.id, - ); - expect(hasWinner, false); - }); - - test('Fetching the winner of a match works correctly', () async { - await database.matchDao.addMatch(match: testMatch1); - - final winner = await database.matchDao.getWinner(matchId: testMatch1.id); - if (winner == null) { - fail('Winner is null'); - } else { - expect(winner.id, testMatch1.winner!.id); - expect(winner.name, testMatch1.winner!.name); - expect(winner.createdAt, testMatch1.winner!.createdAt); - } - }); - - test('Updating the winner of a match works correctly', () async { - await database.matchDao.addMatch(match: testMatch1); - - final winner = await database.matchDao.getWinner(matchId: testMatch1.id); - if (winner == null) { - fail('Winner is null'); - } else { - expect(winner.id, testMatch1.winner!.id); - expect(winner.name, testMatch1.winner!.name); - expect(winner.createdAt, testMatch1.winner!.createdAt); - expect(winner.id, testPlayer4.id); - expect(winner.id != testPlayer5.id, true); - } - - await database.matchDao.setWinner( - matchId: testMatch1.id, - winnerId: testPlayer5.id, - ); - - final newWinner = await database.matchDao.getWinner( - matchId: testMatch1.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.matchDao.addMatch(match: testMatch2); - - var hasWinner = await database.matchDao.hasWinner(matchId: testMatch2.id); - expect(hasWinner, true); - - await database.matchDao.removeWinner(matchId: testMatch2.id); - - hasWinner = await database.matchDao.hasWinner(matchId: testMatch2.id); - expect(hasWinner, false); - - final removedWinner = await database.matchDao.getWinner( - matchId: testMatch2.id, - ); - - expect(removedWinner, null); - }); - + // Verifies that updateMatchName correctly updates only the name field. test('Renaming a match works correctly', () async { await database.matchDao.addMatch(match: testMatch1); diff --git a/test/db_tests/aggregates/team_test.dart b/test/db_tests/aggregates/team_test.dart new file mode 100644 index 0000000..17ceff9 --- /dev/null +++ b/test/db_tests/aggregates/team_test.dart @@ -0,0 +1,527 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; +import 'package:tallee/data/dto/match.dart'; +import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/data/dto/team.dart'; +import 'package:tallee/core/enums.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Team testTeam1; + late Team testTeam2; + late Team testTeam3; + late Game testGame1; + late Game testGame2; + final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() async { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + testTeam1 = Team( + name: 'Team Alpha', + members: [testPlayer1, testPlayer2], + ); + testTeam2 = Team( + name: 'Team Beta', + members: [testPlayer3, testPlayer4], + ); + testTeam3 = Team( + name: 'Team Gamma', + members: [testPlayer1, testPlayer3], + ); + testGame1 = Game(name: 'Game 1', ruleset: Ruleset.singleWinner, description: 'Test game 1', color: GameColor.blue, icon: ''); + testGame2 = Game(name: 'Game 2', ruleset: Ruleset.highestScore, description: 'Test game 2', color: GameColor.red, icon: ''); + }); + + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], + ); + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + }); + + tearDown(() async { + await database.close(); + }); + + group('Team Tests', () { + + // Verifies that a single team can be added and retrieved with all fields intact. + test('Adding and fetching a single team works correctly', () async { + final added = await database.teamDao.addTeam(team: testTeam1); + expect(added, true); + + final fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(fetchedTeam.id, testTeam1.id); + expect(fetchedTeam.name, testTeam1.name); + expect(fetchedTeam.createdAt, testTeam1.createdAt); + }); + + // Verifies that multiple teams can be added at once and retrieved correctly. + test('Adding and fetching multiple teams works correctly', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2, testTeam3], + ); + + final allTeams = await database.teamDao.getAllTeams(); + expect(allTeams.length, 3); + + final testTeams = { + testTeam1.id: testTeam1, + testTeam2.id: testTeam2, + testTeam3.id: testTeam3, + }; + + for (final team in allTeams) { + final testTeam = testTeams[team.id]!; + + expect(team.id, testTeam.id); + expect(team.name, testTeam.name); + expect(team.createdAt, testTeam.createdAt); + } + }); + + // Verifies that adding the same team twice does not create duplicates and returns false. + test('Adding the same team twice does not create duplicates', () async { + await database.teamDao.addTeam(team: testTeam1); + final addedAgain = await database.teamDao.addTeam(team: testTeam1); + + expect(addedAgain, false); + + final teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 1); + }); + + // Verifies that teamExists returns correct boolean based on team presence. + test('Team existence check works correctly', () async { + var teamExists = await database.teamDao.teamExists(teamId: testTeam1.id); + expect(teamExists, false); + + await database.teamDao.addTeam(team: testTeam1); + + teamExists = await database.teamDao.teamExists(teamId: testTeam1.id); + expect(teamExists, true); + }); + + // Verifies that deleteTeam removes the team and returns true. + test('Deleting a team works correctly', () async { + await database.teamDao.addTeam(team: testTeam1); + + final teamDeleted = await database.teamDao.deleteTeam( + teamId: testTeam1.id, + ); + expect(teamDeleted, true); + + final teamExists = await database.teamDao.teamExists( + teamId: testTeam1.id, + ); + expect(teamExists, false); + }); + + // Verifies that deleteTeam returns false for a non-existent team ID. + test('Deleting a non-existent team returns false', () async { + final teamDeleted = await database.teamDao.deleteTeam( + teamId: 'non-existent-id', + ); + expect(teamDeleted, false); + }); + + // Verifies that getTeamCount returns correct count through add/delete operations. + test('Getting the team count works correctly', () async { + var teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 0); + + await database.teamDao.addTeam(team: testTeam1); + + teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 1); + + await database.teamDao.addTeam(team: testTeam2); + + teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 2); + + await database.teamDao.deleteTeam(teamId: testTeam1.id); + + teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 1); + + await database.teamDao.deleteTeam(teamId: testTeam2.id); + + teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 0); + }); + + // Verifies that updateTeamName correctly updates only the name field. + test('Updating team name works correctly', () async { + await database.teamDao.addTeam(team: testTeam1); + + var fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + expect(fetchedTeam.name, testTeam1.name); + + const newName = 'Updated Team Name'; + await database.teamDao.updateTeamName( + teamId: testTeam1.id, + newName: newName, + ); + + fetchedTeam = await database.teamDao.getTeamById(teamId: testTeam1.id); + expect(fetchedTeam.name, newName); + }); + + // Verifies that deleteAllTeams removes all teams from the database. + test('Deleting all teams works correctly', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2, testTeam3], + ); + + var teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 3); + + final deleted = await database.teamDao.deleteAllTeams(); + expect(deleted, true); + + teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 0); + }); + + // Verifies that deleteAllTeams returns false when no teams exist. + test('Deleting all teams when empty returns false', () async { + final deleted = await database.teamDao.deleteAllTeams(); + expect(deleted, false); + }); + + // Verifies that addTeamsAsList returns false when given an empty list. + test('Adding teams as list with empty list returns false', () async { + final added = await database.teamDao.addTeamsAsList(teams: []); + expect(added, false); + }); + + // Verifies that addTeamsAsList with duplicate IDs ignores duplicates and keeps the first. + test('Adding teams with duplicate IDs ignores duplicates', () async { + final duplicateTeam = Team( + id: testTeam1.id, + name: 'Duplicate Team', + members: [testPlayer4], + ); + + await database.teamDao.addTeamsAsList( + teams: [testTeam1, duplicateTeam, testTeam2], + ); + + final teamCount = await database.teamDao.getTeamCount(); + expect(teamCount, 2); + + // The first one should be kept (insertOrIgnore) + final fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + expect(fetchedTeam.name, testTeam1.name); + }); + + // Verifies that getAllTeams returns empty list when no teams exist. + test('Getting all teams when empty returns empty list', () async { + final allTeams = await database.teamDao.getAllTeams(); + expect(allTeams.isEmpty, true); + }); + + // Verifies that getTeamById throws exception for non-existent team. + test('Getting non-existent team throws exception', () async { + expect( + () => database.teamDao.getTeamById(teamId: 'non-existent-id'), + throwsA(isA()), + ); + }); + + // Verifies that updating team name preserves other fields. + test('Updating team name preserves other team fields', () async { + await database.teamDao.addTeam(team: testTeam1); + final originalTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + final originalCreatedAt = originalTeam.createdAt; + + const newName = 'Brand New Team Name'; + await database.teamDao.updateTeamName( + teamId: testTeam1.id, + newName: newName, + ); + + final updatedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(updatedTeam.name, newName); + expect(updatedTeam.id, testTeam1.id); + expect(updatedTeam.createdAt, originalCreatedAt); + }); + + // Verifies that team name can be updated to an empty string. + test('Updating team name to empty string works', () async { + await database.teamDao.addTeam(team: testTeam1); + + await database.teamDao.updateTeamName( + teamId: testTeam1.id, + newName: '', + ); + + final updatedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(updatedTeam.name, ''); + }); + + // Verifies that team name can be updated to a very long string. + test('Updating team name to long string works', () async { + await database.teamDao.addTeam(team: testTeam1); + final longName = 'A' * 500; // 500 character name + + await database.teamDao.updateTeamName( + teamId: testTeam1.id, + newName: longName, + ); + + final updatedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(updatedTeam.name, longName); + expect(updatedTeam.name.length, 500); + }); + + // Verifies that updating non-existent team name doesn't throw error. + test('Updating non-existent team name completes without error', () async { + expect( + () => database.teamDao.updateTeamName( + teamId: 'non-existent-id', + newName: 'New Name', + ), + returnsNormally, + ); + }); + + // Verifies that deleteTeam only affects the specified team. + test('Deleting one team does not affect other teams', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2, testTeam3], + ); + + await database.teamDao.deleteTeam(teamId: testTeam2.id); + + final allTeams = await database.teamDao.getAllTeams(); + expect(allTeams.length, 2); + expect(allTeams.any((t) => t.id == testTeam1.id), true); + expect(allTeams.any((t) => t.id == testTeam2.id), false); + expect(allTeams.any((t) => t.id == testTeam3.id), true); + }); + + // Verifies that teams with overlapping members are independent. + test('Teams with overlapping members are independent', () async { + // Create two matches since player_match has primary key {playerId, matchId} + final match1 = Match(name: 'Match 1', game: testGame1, notes: ''); + final match2 = Match(name: 'Match 2', game: testGame2, notes: ''); + await database.matchDao.addMatch(match: match1); + await database.matchDao.addMatch(match: match2); + + // Add teams to database + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam3], + ); + + // Associate players with teams through match1 + // testTeam1: player1, player2 + await database.playerMatchDao.addPlayerToMatch( + playerId: testPlayer1.id, + matchId: match1.id, + teamId: testTeam1.id, + score: 0, + ); + await database.playerMatchDao.addPlayerToMatch( + playerId: testPlayer2.id, + matchId: match1.id, + teamId: testTeam1.id, + score: 0, + ); + + // Associate players with teams through match2 + // testTeam3: player1, player3 (overlapping player1) + await database.playerMatchDao.addPlayerToMatch( + playerId: testPlayer1.id, + matchId: match2.id, + teamId: testTeam3.id, + score: 0, + ); + await database.playerMatchDao.addPlayerToMatch( + playerId: testPlayer3.id, + matchId: match2.id, + teamId: testTeam3.id, + score: 0, + ); + + final team1 = await database.teamDao.getTeamById(teamId: testTeam1.id); + final team3 = await database.teamDao.getTeamById(teamId: testTeam3.id); + + expect(team1.members.length, 2); + expect(team3.members.length, 2); + expect(team1.members.any((p) => p.id == testPlayer1.id), true); + expect(team3.members.any((p) => p.id == testPlayer1.id), true); + }); + + // Verifies that adding teams sequentially works correctly. + test('Adding teams sequentially maintains correct count', () async { + var count = await database.teamDao.getTeamCount(); + expect(count, 0); + + await database.teamDao.addTeam(team: testTeam1); + count = await database.teamDao.getTeamCount(); + expect(count, 1); + + await database.teamDao.addTeam(team: testTeam2); + count = await database.teamDao.getTeamCount(); + expect(count, 2); + + await database.teamDao.addTeam(team: testTeam3); + count = await database.teamDao.getTeamCount(); + expect(count, 3); + }); + + // Verifies that getAllTeams returns all teams with correct data. + test('Getting all teams returns all teams with correct data', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2, testTeam3], + ); + + final allTeams = await database.teamDao.getAllTeams(); + + expect(allTeams.length, 3); + expect( + allTeams.map((t) => t.id).toSet(), + {testTeam1.id, testTeam2.id, testTeam3.id}, + ); + }); + + // Verifies that teamExists returns false for deleted teams. + test('Team existence returns false after deletion', () async { + await database.teamDao.addTeam(team: testTeam1); + expect(await database.teamDao.teamExists(teamId: testTeam1.id), true); + + await database.teamDao.deleteTeam(teamId: testTeam1.id); + expect(await database.teamDao.teamExists(teamId: testTeam1.id), false); + }); + + // Verifies that adding multiple teams in batch then deleting returns correct count. + test('Batch add then partial delete maintains correct count', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2, testTeam3], + ); + + expect(await database.teamDao.getTeamCount(), 3); + + await database.teamDao.deleteTeam(teamId: testTeam1.id); + expect(await database.teamDao.getTeamCount(), 2); + + await database.teamDao.deleteTeam(teamId: testTeam3.id); + expect(await database.teamDao.getTeamCount(), 1); + }); + + // Verifies that deleteAllTeams with single team works. + test('Deleting all teams with single team returns true', () async { + await database.teamDao.addTeam(team: testTeam1); + expect(await database.teamDao.getTeamCount(), 1); + + final deleted = await database.teamDao.deleteAllTeams(); + expect(deleted, true); + expect(await database.teamDao.getTeamCount(), 0); + }); + + // Verifies that addTeam after deleteAllTeams works correctly. + test('Adding team after deleteAllTeams works correctly', () async { + await database.teamDao.addTeamsAsList( + teams: [testTeam1, testTeam2], + ); + expect(await database.teamDao.getTeamCount(), 2); + + await database.teamDao.deleteAllTeams(); + expect(await database.teamDao.getTeamCount(), 0); + + final added = await database.teamDao.addTeam(team: testTeam3); + expect(added, true); + expect(await database.teamDao.getTeamCount(), 1); + + final fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam3.id, + ); + expect(fetchedTeam.name, testTeam3.name); + }); + + // Verifies that addTeamsAsList with partial duplicates ignores duplicates. + test('Adding teams with some duplicates ignores only duplicates', () async { + await database.teamDao.addTeam(team: testTeam1); + + final duplicateTeam1 = Team( + id: testTeam1.id, + name: 'Different Name', + members: [testPlayer3], + ); + + await database.teamDao.addTeamsAsList( + teams: [duplicateTeam1, testTeam2, testTeam3], + ); + + final allTeams = await database.teamDao.getAllTeams(); + expect(allTeams.length, 3); + + // Verify testTeam1 retained original name (was inserted first) + final team1 = await database.teamDao.getTeamById(teamId: testTeam1.id); + expect(team1.name, testTeam1.name); + }); + + // Verifies that team IDs are preserved correctly. + test('Team IDs are preserved through add and retrieve', () async { + await database.teamDao.addTeam(team: testTeam1); + + final fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(fetchedTeam.id, testTeam1.id); + }); + + // Verifies that createdAt timestamps are preserved. + test('Team createdAt timestamps are preserved', () async { + await database.teamDao.addTeam(team: testTeam1); + + final fetchedTeam = await database.teamDao.getTeamById( + teamId: testTeam1.id, + ); + + expect(fetchedTeam.createdAt, testTeam1.createdAt); + }); + }); +} \ No newline at end of file diff --git a/test/db_tests/entities/game_test.dart b/test/db_tests/entities/game_test.dart new file mode 100644 index 0000000..924a60b --- /dev/null +++ b/test/db_tests/entities/game_test.dart @@ -0,0 +1,542 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; + +void main() { + late AppDatabase database; + late Game testGame1; + late Game testGame2; + late Game testGame3; + final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testGame1 = Game( + name: 'Chess', + ruleset: Ruleset.singleWinner, + description: 'A classic strategy game', + color: GameColor.blue, + icon: 'chess_icon', + ); + testGame2 = Game( + id: 'game2', + name: 'Poker', + ruleset: Ruleset.multipleWinners, + description: 'Card game with multiple winners', + color: GameColor.red, + icon: 'poker_icon', + ); + testGame3 = Game( + id: 'game3', + name: 'Monopoly', + ruleset: Ruleset.highestScore, + description: 'A board game about real estate', + color: GameColor.orange, + icon: '', + ); + }); + }); + + tearDown(() async { + await database.close(); + }); + + group('Game Tests', () { + + // Verifies that getAllGames returns an empty list when the database has no games. + test('getAllGames returns empty list when no games exist', () async { + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that a single game can be added and retrieved with all fields intact. + test('Adding and fetching a single game works correctly', () async { + await database.gameDao.addGame(game: testGame1); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + expect(allGames.first.id, testGame1.id); + expect(allGames.first.name, testGame1.name); + expect(allGames.first.ruleset, testGame1.ruleset); + expect(allGames.first.description, testGame1.description); + expect(allGames.first.color, testGame1.color); + expect(allGames.first.icon, testGame1.icon); + expect(allGames.first.createdAt, testGame1.createdAt); + }); + + // Verifies that multiple games can be added and retrieved correctly. + test('Adding and fetching multiple games works correctly', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + await database.gameDao.addGame(game: testGame3); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 3); + + final names = allGames.map((g) => g.name).toList(); + expect(names, containsAll(['Chess', 'Poker', 'Monopoly'])); + }); + + // Verifies that getGameById returns the correct game with all properties. + test('getGameById returns correct game', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.addGame(game: testGame2); + + final game = await database.gameDao.getGameById(gameId: testGame2.id); + expect(game.id, testGame2.id); + expect(game.name, testGame2.name); + expect(game.ruleset, testGame2.ruleset); + expect(game.description, testGame2.description); + expect(game.color, testGame2.color); + expect(game.icon, testGame2.icon); + }); + + // Verifies that getGameById throws a StateError when the game doesn't exist. + test('getGameById throws exception for non-existent game', () async { + expect( + () => database.gameDao.getGameById(gameId: 'non-existent-id'), + throwsA(isA()), + ); + }); + + // Verifies that addGame returns true when a game is successfully added. + test('addGame returns true when game is added successfully', () async { + final result = await database.gameDao.addGame(game: testGame1); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + }); + + // Verifies that addGame returns false when trying to add a duplicate game. + test('addGame returns false when game already exists', () async { + final firstAdd = await database.gameDao.addGame(game: testGame1); + expect(firstAdd, true); + + final secondAdd = await database.gameDao.addGame(game: testGame1); + expect(secondAdd, false); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 1); + }); + + // Verifies that a game with empty optional fields can be added and retrieved. + test('addGame handles game with null optional fields', () async { + final gameWithNulls = Game(name: 'Simple Game', ruleset: Ruleset.lowestScore, description: 'A simple game', color: GameColor.green, icon: ''); + final result = await database.gameDao.addGame(game: gameWithNulls); + expect(result, true); + + final fetchedGame = await database.gameDao.getGameById( + gameId: gameWithNulls.id, + ); + expect(fetchedGame.name, 'Simple Game'); + expect(fetchedGame.description, 'A simple game'); + expect(fetchedGame.color, GameColor.green); + expect(fetchedGame.icon, ''); + }); + + // Verifies that multiple games can be added at once using addGamesAsList. + test('addGamesAsList adds multiple games correctly', () async { + final result = await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 3); + }); + + // Verifies that addGamesAsList returns false when given an empty list. + test('addGamesAsList returns false for empty list', () async { + final result = await database.gameDao.addGamesAsList(games: []); + expect(result, false); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 0); + }); + + // Verifies that addGamesAsList ignores duplicate games when adding. + test('addGamesAsList ignores duplicate games', () async { + await database.gameDao.addGame(game: testGame1); + + final result = await database.gameDao.addGamesAsList( + games: [testGame1, testGame2], + ); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 2); + }); + + // Verifies that deleteGame returns true and removes the game from database. + test('deleteGame returns true when game is deleted', () async { + await database.gameDao.addGame(game: testGame1); + + final result = await database.gameDao.deleteGame(gameId: testGame1.id); + expect(result, true); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that deleteGame returns false for a non-existent game ID. + test('deleteGame returns false for non-existent game', () async { + final result = await database.gameDao.deleteGame( + gameId: 'non-existent-id', + ); + expect(result, false); + }); + + // Verifies that deleteGame only removes the specified game, leaving others intact. + test('deleteGame only deletes the specified game', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + await database.gameDao.deleteGame(gameId: testGame2.id); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames.length, 2); + expect(allGames.any((g) => g.id == testGame2.id), false); + expect(allGames.any((g) => g.id == testGame1.id), true); + expect(allGames.any((g) => g.id == testGame3.id), true); + }); + + // Verifies that gameExists returns true when the game exists in database. + test('gameExists returns true for existing game', () async { + await database.gameDao.addGame(game: testGame1); + + final exists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(exists, true); + }); + + // Verifies that gameExists returns false for a non-existent game ID. + test('gameExists returns false for non-existent game', () async { + final exists = await database.gameDao.gameExists( + gameId: 'non-existent-id', + ); + expect(exists, false); + }); + + // Verifies that gameExists returns false after a game has been deleted. + test('gameExists returns false after game is deleted', () async { + await database.gameDao.addGame(game: testGame1); + await database.gameDao.deleteGame(gameId: testGame1.id); + + final exists = await database.gameDao.gameExists(gameId: testGame1.id); + expect(exists, false); + }); + + // Verifies that updateGameName correctly updates only the name field. + test('updateGameName updates the name correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameName( + gameId: testGame1.id, + newName: 'Updated Chess', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.name, 'Updated Chess'); + expect(updatedGame.ruleset, testGame1.ruleset); + }); + + // Verifies that updateGameName does nothing when game doesn't exist. + test('updateGameName does nothing for non-existent game', () async { + await database.gameDao.updateGameName( + gameId: 'non-existent-id', + newName: 'New Name', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that updateGameRuleset correctly updates only the ruleset field. + test('updateGameRuleset updates the ruleset correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameRuleset( + gameId: testGame1.id, + newRuleset: Ruleset.highestScore, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.ruleset, Ruleset.highestScore); + expect(updatedGame.name, testGame1.name); + }); + + // Verifies that updateGameRuleset does nothing when game doesn't exist. + test('updateGameRuleset does nothing for non-existent game', () async { + await database.gameDao.updateGameRuleset( + gameId: 'non-existent-id', + newRuleset: Ruleset.lowestScore, + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that updateGameDescription correctly updates the description. + test('updateGameDescription updates the description correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: 'An updated description', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.description, 'An updated description'); + }); + + // Verifies that updateGameDescription can set the description to an empty string. + test('updateGameDescription can set description to empty string', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: '', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.description, ''); + }); + + // Verifies that updateGameDescription does nothing when game doesn't exist. + test('updateGameDescription does nothing for non-existent game', () async { + await database.gameDao.updateGameDescription( + gameId: 'non-existent-id', + newDescription: 'New Description', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that updateGameColor correctly updates the color value. + test('updateGameColor updates the color correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameColor( + gameId: testGame1.id, + newColor: GameColor.green, + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.color, GameColor.green); + }); + + // Verifies that updateGameColor does nothing when game doesn't exist. + test('updateGameColor does nothing for non-existent game', () async { + await database.gameDao.updateGameColor( + gameId: 'non-existent-id', + newColor: GameColor.green, + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that updateGameIcon correctly updates the icon value. + test('updateGameIcon updates the icon correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameIcon( + gameId: testGame1.id, + newIcon: 'new_chess_icon', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.icon, 'new_chess_icon'); + }); + + // Verifies that updateGameIcon can update the icon. + test('updateGameIcon updates icon correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameIcon( + gameId: testGame1.id, + newIcon: 'new_icon', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.icon, 'new_icon'); + }); + + // Verifies that updateGameIcon does nothing when game doesn't exist. + test('updateGameIcon does nothing for non-existent game', () async { + await database.gameDao.updateGameIcon( + gameId: 'non-existent-id', + newIcon: 'some_icon', + ); + + final allGames = await database.gameDao.getAllGames(); + expect(allGames, isEmpty); + }); + + // Verifies that getGameCount returns 0 when no games exist. + test('getGameCount returns 0 when no games exist', () async { + final count = await database.gameDao.getGameCount(); + expect(count, 0); + }); + + // Verifies that getGameCount returns the correct count after adding games. + test('getGameCount returns correct count after adding games', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + final count = await database.gameDao.getGameCount(); + expect(count, 3); + }); + + // Verifies that getGameCount updates correctly after deleting a game. + test('getGameCount updates correctly after deletion', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2], + ); + + final countBefore = await database.gameDao.getGameCount(); + expect(countBefore, 2); + + await database.gameDao.deleteGame(gameId: testGame1.id); + + final countAfter = await database.gameDao.getGameCount(); + expect(countAfter, 1); + }); + + // Verifies that deleteAllGames removes all games from the database. + test('deleteAllGames removes all games', () async { + await database.gameDao.addGamesAsList( + games: [testGame1, testGame2, testGame3], + ); + + final countBefore = await database.gameDao.getGameCount(); + expect(countBefore, 3); + + final result = await database.gameDao.deleteAllGames(); + expect(result, true); + + final countAfter = await database.gameDao.getGameCount(); + expect(countAfter, 0); + }); + + // Verifies that deleteAllGames returns false when no games exist. + test('deleteAllGames returns false when no games exist', () async { + final result = await database.gameDao.deleteAllGames(); + expect(result, false); + }); + + // Verifies that games with special characters (quotes, emojis) are stored correctly. + test('Game with special characters in name is stored correctly', () async { + final specialGame = Game( + name: 'Game\'s & "Special" ', + ruleset: Ruleset.multipleWinners, + description: 'Description with émojis 🎮🎲', + color: GameColor.purple, + icon: '', + ); + await database.gameDao.addGame(game: specialGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: specialGame.id, + ); + expect(fetchedGame.name, 'Game\'s & "Special" '); + expect(fetchedGame.description, 'Description with émojis 🎮🎲'); + }); + + // Verifies that games with empty string fields are stored and retrieved correctly. + test('Game with empty string fields is stored correctly', () async { + final emptyGame = Game( + name: '', + ruleset: Ruleset.singleWinner, + description: '', + icon: '', + color: GameColor.red, + ); + await database.gameDao.addGame(game: emptyGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: emptyGame.id, + ); + expect(fetchedGame.name, ''); + expect(fetchedGame.ruleset, Ruleset.singleWinner); + expect(fetchedGame.description, ''); + expect(fetchedGame.icon, ''); + }); + + // Verifies that games with very long strings (10000 chars) are handled correctly. + test('Game with very long strings is stored correctly', () async { + final longString = 'A' * 10000; + final longGame = Game( + name: longString, + description: longString, + ruleset: Ruleset.multipleWinners, + color: GameColor.yellow, + icon: '', + ); + await database.gameDao.addGame(game: longGame); + + final fetchedGame = await database.gameDao.getGameById( + gameId: longGame.id, + ); + expect(fetchedGame.name.length, 10000); + expect(fetchedGame.description.length, 10000); + expect(fetchedGame.ruleset, Ruleset.multipleWinners); + }); + + // Verifies that multiple sequential updates to the same game work correctly. + test('Multiple updates to the same game work correctly', () async { + await database.gameDao.addGame(game: testGame1); + + await database.gameDao.updateGameName( + gameId: testGame1.id, + newName: 'Updated Name', + ); + await database.gameDao.updateGameColor( + gameId: testGame1.id, + newColor: GameColor.teal, + ); + await database.gameDao.updateGameDescription( + gameId: testGame1.id, + newDescription: 'Updated Description', + ); + + final updatedGame = await database.gameDao.getGameById( + gameId: testGame1.id, + ); + expect(updatedGame.name, 'Updated Name'); + expect(updatedGame.color, GameColor.teal); + expect(updatedGame.description, 'Updated Description'); + expect(updatedGame.ruleset, testGame1.ruleset); + expect(updatedGame.icon, testGame1.icon); + }); + }); +} diff --git a/test/db_tests/entities/player_test.dart b/test/db_tests/entities/player_test.dart new file mode 100644 index 0000000..9d7fa77 --- /dev/null +++ b/test/db_tests/entities/player_test.dart @@ -0,0 +1,377 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Test Player', description: ''); + testPlayer2 = Player(name: 'Second Player', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + }); + }); + tearDown(() async { + await database.close(); + }); + + group('Player Tests', () { + + // Verifies that players can be added and retrieved with all fields intact. + test('Adding and fetching single player works correctly', () async { + await database.playerDao.addPlayer(player: testPlayer1); + await database.playerDao.addPlayer(player: testPlayer2); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers.length, 2); + + final fetchedPlayer1 = allPlayers.firstWhere( + (g) => g.id == testPlayer1.id, + ); + expect(fetchedPlayer1.name, testPlayer1.name); + expect(fetchedPlayer1.createdAt, testPlayer1.createdAt); + + final fetchedPlayer2 = allPlayers.firstWhere( + (g) => g.id == testPlayer2.id, + ); + expect(fetchedPlayer2.name, testPlayer2.name); + expect(fetchedPlayer2.createdAt, testPlayer2.createdAt); + }); + + // Verifies that multiple players can be added at once and retrieved correctly. + test('Adding and fetching multiple players works correctly', () async { + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], + ); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers.length, 4); + + // Map for connecting fetched players with expected players + final testPlayers = { + testPlayer1.id: testPlayer1, + testPlayer2.id: testPlayer2, + testPlayer3.id: testPlayer3, + testPlayer4.id: testPlayer4, + }; + + for (final player in allPlayers) { + final testPlayer = testPlayers[player.id]!; + + expect(player.id, testPlayer.id); + expect(player.name, testPlayer.name); + expect(player.createdAt, testPlayer.createdAt); + } + }); + + // Verifies that adding the same player twice does not create duplicates. + test('Adding the same player twice does not create duplicates', () async { + await database.playerDao.addPlayer(player: testPlayer1); + await database.playerDao.addPlayer(player: testPlayer1); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers.length, 1); + }); + + // Verifies that playerExists returns correct boolean based on player presence. + test('Player existence check works correctly', () async { + var playerExists = await database.playerDao.playerExists( + playerId: testPlayer1.id, + ); + expect(playerExists, false); + + await database.playerDao.addPlayer(player: testPlayer1); + + playerExists = await database.playerDao.playerExists( + playerId: testPlayer1.id, + ); + expect(playerExists, true); + }); + + // Verifies that deletePlayer removes the player and returns true. + test('Deleting a player works correctly', () async { + await database.playerDao.addPlayer(player: testPlayer1); + final playerDeleted = await database.playerDao.deletePlayer( + playerId: testPlayer1.id, + ); + expect(playerDeleted, true); + + final playerExists = await database.playerDao.playerExists( + playerId: testPlayer1.id, + ); + expect(playerExists, false); + }); + + // Verifies that updatePlayerName correctly updates only the name field. + test('Updating a player name works correctly', () async { + await database.playerDao.addPlayer(player: testPlayer1); + + const newPlayerName = 'new player name'; + + await database.playerDao.updatePlayerName( + playerId: testPlayer1.id, + newName: newPlayerName, + ); + + final result = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(result.name, newPlayerName); + }); + + // Verifies that getPlayerCount returns correct count through add/delete operations. + test('Getting the player count works correctly', () async { + var playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 0); + + await database.playerDao.addPlayer(player: testPlayer1); + + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 1); + + await database.playerDao.addPlayer(player: testPlayer2); + + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 2); + + await database.playerDao.deletePlayer(playerId: testPlayer1.id); + + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 1); + + await database.playerDao.deletePlayer(playerId: testPlayer2.id); + + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 0); + }); + + // Verifies that getAllPlayers returns an empty list when no players exist. + test('getAllPlayers returns empty list when no players exist', () async { + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers, isEmpty); + }); + + // Verifies that getPlayerById returns the correct player. + test('getPlayerById returns correct player', () async { + await database.playerDao.addPlayer(player: testPlayer1); + await database.playerDao.addPlayer(player: testPlayer2); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + + expect(fetchedPlayer.id, testPlayer1.id); + expect(fetchedPlayer.name, testPlayer1.name); + expect(fetchedPlayer.createdAt, testPlayer1.createdAt); + expect(fetchedPlayer.description, testPlayer1.description); + }); + + // Verifies that getPlayerById throws StateError for non-existent player ID. + test('getPlayerById throws exception for non-existent player', () async { + expect( + () => database.playerDao.getPlayerById(playerId: 'non-existent-id'), + throwsA(isA()), + ); + }); + + // Verifies that addPlayer returns false when trying to add a duplicate player. + test('addPlayer returns false when player already exists', () async { + final firstAdd = await database.playerDao.addPlayer(player: testPlayer1); + expect(firstAdd, true); + + final secondAdd = await database.playerDao.addPlayer(player: testPlayer1); + expect(secondAdd, false); + }); + + // Verifies that addPlayersAsList handles empty list correctly. + test('addPlayersAsList handles empty list correctly', () async { + final result = await database.playerDao.addPlayersAsList(players: []); + expect(result, false); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers, isEmpty); + }); + + // Verifies that addPlayersAsList ignores duplicate player IDs. + test('addPlayersAsList with duplicate IDs ignores duplicates', () async { + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer1, testPlayer2], + ); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers.length, 2); + }); + + // Verifies that deletePlayer returns false for non-existent player. + test('deletePlayer returns false for non-existent player', () async { + final result = await database.playerDao.deletePlayer( + playerId: 'non-existent-id', + ); + expect(result, false); + }); + + // Verifies that updatePlayerName does nothing for non-existent player (no exception). + test('updatePlayerName does nothing for non-existent player', () async { + // Should not throw, just do nothing + await database.playerDao.updatePlayerName( + playerId: 'non-existent-id', + newName: 'New Name', + ); + + final allPlayers = await database.playerDao.getAllPlayers(); + expect(allPlayers, isEmpty); + }); + + // Verifies that deleteAllPlayers removes all players. + test('deleteAllPlayers removes all players', () async { + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer2, testPlayer3], + ); + + var playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 3); + + final result = await database.playerDao.deleteAllPlayers(); + expect(result, true); + + playerCount = await database.playerDao.getPlayerCount(); + expect(playerCount, 0); + }); + + // Verifies that deleteAllPlayers returns false when no players exist. + test('deleteAllPlayers returns false when no players exist', () async { + final result = await database.playerDao.deleteAllPlayers(); + expect(result, false); + }); + + // Verifies that a player with special characters in name is stored correctly. + test('Player with special characters in name is stored correctly', () async { + final specialPlayer = Player(name: 'Test!@#\$%^&*()_+-=[]{}|;\':",.<>?/`~', description: ''); + + await database.playerDao.addPlayer(player: specialPlayer); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: specialPlayer.id, + ); + expect(fetchedPlayer.name, specialPlayer.name); + }); + + // Verifies that a player with description is stored correctly. + test('Player with description is stored correctly', () async { + final playerWithDescription = Player( + name: 'Described Player', + description: 'This is a test description', + ); + + await database.playerDao.addPlayer(player: playerWithDescription); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: playerWithDescription.id, + ); + expect(fetchedPlayer.name, playerWithDescription.name); + expect(fetchedPlayer.description, playerWithDescription.description); + }); + + // Verifies that a player with null description is stored correctly. + test('Player with null description is stored correctly', () async { + final playerWithoutDescription = Player(name: 'No Description Player', description: ''); + + await database.playerDao.addPlayer(player: playerWithoutDescription); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: playerWithoutDescription.id, + ); + expect(fetchedPlayer.description, ''); + }); + + // Verifies that multiple updates to the same player work correctly. + test('Multiple updates to the same player work correctly', () async { + await database.playerDao.addPlayer(player: testPlayer1); + + await database.playerDao.updatePlayerName( + playerId: testPlayer1.id, + newName: 'First Update', + ); + + var fetchedPlayer = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(fetchedPlayer.name, 'First Update'); + + await database.playerDao.updatePlayerName( + playerId: testPlayer1.id, + newName: 'Second Update', + ); + + fetchedPlayer = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(fetchedPlayer.name, 'Second Update'); + + await database.playerDao.updatePlayerName( + playerId: testPlayer1.id, + newName: 'Third Update', + ); + + fetchedPlayer = await database.playerDao.getPlayerById( + playerId: testPlayer1.id, + ); + expect(fetchedPlayer.name, 'Third Update'); + }); + + // Verifies that a player with empty string name is stored correctly. + test('Player with empty string name is stored correctly', () async { + final emptyNamePlayer = Player(name: '', description: ''); + + await database.playerDao.addPlayer(player: emptyNamePlayer); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: emptyNamePlayer.id, + ); + expect(fetchedPlayer.name, ''); + }); + + // Verifies that a player with very long name is stored correctly. + test('Player with very long name is stored correctly', () async { + final longName = 'A' * 1000; + final longNamePlayer = Player(name: longName, description: ''); + + await database.playerDao.addPlayer(player: longNamePlayer); + + final fetchedPlayer = await database.playerDao.getPlayerById( + playerId: longNamePlayer.id, + ); + expect(fetchedPlayer.name, longName); + }); + + // Verifies that addPlayer returns true on first add. + test('addPlayer returns true when player is added successfully', () async { + final result = await database.playerDao.addPlayer(player: testPlayer1); + expect(result, true); + + final playerExists = await database.playerDao.playerExists( + playerId: testPlayer1.id, + ); + expect(playerExists, true); + }); + }); +} diff --git a/test/db_tests/group_match_test.dart b/test/db_tests/group_match_test.dart deleted file mode 100644 index 8c5e6e4..0000000 --- a/test/db_tests/group_match_test.dart +++ /dev/null @@ -1,221 +0,0 @@ -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart' hide isNotNull; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/dto/group.dart'; -import 'package:tallee/data/dto/match.dart'; -import 'package:tallee/data/dto/player.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - late Player testPlayer5; - late Group testGroup1; - late Group testGroup2; - late Match testMatchWithGroup; - late Match testMatchWithPlayers; - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() async { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testPlayer5 = Player(name: 'Eve'); - testGroup1 = Group( - name: 'Test Group', - members: [testPlayer1, testPlayer2, testPlayer3], - ); - testGroup2 = Group( - name: 'Test Group', - members: [testPlayer3, testPlayer2], - ); - testMatchWithPlayers = Match( - name: 'Test Match with Players', - players: [testPlayer4, testPlayer5], - ); - testMatchWithGroup = Match( - name: 'Test Match with Group', - group: testGroup1, - ); - }); - await database.playerDao.addPlayersAsList( - players: [ - testPlayer1, - testPlayer2, - testPlayer3, - testPlayer4, - testPlayer5, - ], - ); - await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); - }); - tearDown(() async { - await database.close(); - }); - group('Group-Match Tests', () { - test('matchHasGroup() has group works correctly', () async { - await database.matchDao.addMatch(match: testMatchWithPlayers); - await database.groupDao.addGroup(group: testGroup1); - - var matchHasGroup = await database.groupMatchDao.matchHasGroup( - matchId: testMatchWithPlayers.id, - ); - - expect(matchHasGroup, false); - - await database.groupMatchDao.addGroupToMatch( - matchId: testMatchWithPlayers.id, - groupId: testGroup1.id, - ); - - matchHasGroup = await database.groupMatchDao.matchHasGroup( - matchId: testMatchWithPlayers.id, - ); - - expect(matchHasGroup, true); - }); - - test('Adding a group to a match works correctly', () async { - await database.matchDao.addMatch(match: testMatchWithPlayers); - await database.groupDao.addGroup(group: testGroup1); - await database.groupMatchDao.addGroupToMatch( - matchId: testMatchWithPlayers.id, - groupId: testGroup1.id, - ); - - var groupAdded = await database.groupMatchDao.isGroupInMatch( - matchId: testMatchWithPlayers.id, - groupId: testGroup1.id, - ); - expect(groupAdded, true); - - groupAdded = await database.groupMatchDao.isGroupInMatch( - matchId: testMatchWithPlayers.id, - groupId: '', - ); - expect(groupAdded, false); - }); - - test('Removing group from match works correctly', () async { - await database.matchDao.addMatch(match: testMatchWithGroup); - - final groupToRemove = testMatchWithGroup.group!; - - final removed = await database.groupMatchDao.removeGroupFromMatch( - groupId: groupToRemove.id, - matchId: testMatchWithGroup.id, - ); - expect(removed, true); - - final result = await database.matchDao.getMatchById( - matchId: testMatchWithGroup.id, - ); - expect(result.group, null); - }); - - test('Retrieving group of a match works correctly', () async { - await database.matchDao.addMatch(match: testMatchWithGroup); - final group = await database.groupMatchDao.getGroupOfMatch( - matchId: testMatchWithGroup.id, - ); - - if (group == null) { - fail('Group should not be null'); - } - - 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, 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 match works correctly', () async { - await database.matchDao.addMatch(match: testMatchWithGroup); - - var group = await database.groupMatchDao.getGroupOfMatch( - matchId: testMatchWithGroup.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.groupMatchDao.updateGroupOfMatch( - matchId: testMatchWithGroup.id, - newGroupId: testGroup2.id, - ); - - group = await database.groupMatchDao.getGroupOfMatch( - matchId: testMatchWithGroup.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); - } - } - }); - - test('Adding the same group to seperate matches works correctly', () async { - final match1 = Match(name: 'Match 1', group: testGroup1); - final match2 = Match(name: 'Match 2', group: testGroup1); - - await Future.wait([ - database.matchDao.addMatch(match: match1), - database.matchDao.addMatch(match: match2), - ]); - - final group1 = await database.groupMatchDao.getGroupOfMatch( - matchId: match1.id, - ); - final group2 = await database.groupMatchDao.getGroupOfMatch( - matchId: match2.id, - ); - - expect(group1, isNotNull); - expect(group2, isNotNull); - - final groups = [group1!, group2!]; - for (final group in groups) { - expect(group.members.length, testGroup1.members.length); - expect(group.id, testGroup1.id); - expect(group.name, testGroup1.name); - expect(group.createdAt, testGroup1.createdAt); - } - }); - }); -} diff --git a/test/db_tests/group_test.dart b/test/db_tests/group_test.dart deleted file mode 100644 index d87edf3..0000000 --- a/test/db_tests/group_test.dart +++ /dev/null @@ -1,177 +0,0 @@ -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart'; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/dto/group.dart'; -import 'package:tallee/data/dto/player.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - late Group testGroup1; - late Group testGroup2; - late Group testGroup3; - late Group testGroup4; - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testGroup1 = Group( - name: 'Test Group', - members: [testPlayer1, testPlayer2, testPlayer3], - ); - testGroup2 = Group( - id: 'gr2', - name: 'Second Group', - members: [testPlayer2, testPlayer3, testPlayer4], - ); - testGroup3 = Group( - id: 'gr2', - name: 'Second Group', - members: [testPlayer2, testPlayer4], - ); - testGroup4 = Group( - id: 'gr2', - name: 'Second Group', - members: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], - ); - }); - }); - tearDown(() async { - await database.close(); - }); - group('Group Tests', () { - test('Adding and fetching a single group works correctly', () async { - await database.groupDao.addGroup(group: testGroup1); - - final fetchedGroup = await database.groupDao.getGroupById( - groupId: testGroup1.id, - ); - - expect(fetchedGroup.id, testGroup1.id); - expect(fetchedGroup.name, testGroup1.name); - expect(fetchedGroup.createdAt, testGroup1.createdAt); - - expect(fetchedGroup.members.length, testGroup1.members.length); - for (int i = 0; i < testGroup1.members.length; i++) { - expect(fetchedGroup.members[i].id, testGroup1.members[i].id); - expect(fetchedGroup.members[i].name, testGroup1.members[i].name); - expect( - fetchedGroup.members[i].createdAt, - testGroup1.members[i].createdAt, - ); - } - }); - - test('Adding and fetching multiple groups works correctly', () async { - await database.groupDao.addGroupsAsList( - groups: [testGroup1, testGroup2, testGroup3, testGroup4], - ); - - final allGroups = await database.groupDao.getAllGroups(); - expect(allGroups.length, 2); - - final testGroups = {testGroup1.id: testGroup1, testGroup2.id: testGroup2}; - - for (final group in allGroups) { - final testGroup = testGroups[group.id]!; - - expect(group.id, testGroup.id); - expect(group.name, testGroup.name); - expect(group.createdAt, testGroup.createdAt); - - expect(group.members.length, testGroup.members.length); - for (int i = 0; i < testGroup.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); - } - } - }); - - test('Adding the same group twice does not create duplicates', () async { - await database.groupDao.addGroup(group: testGroup1); - await database.groupDao.addGroup(group: testGroup1); - - final allGroups = await database.groupDao.getAllGroups(); - expect(allGroups.length, 1); - }); - - test('Group existence check works correctly', () async { - var groupExists = await database.groupDao.groupExists( - groupId: testGroup1.id, - ); - expect(groupExists, false); - - await database.groupDao.addGroup(group: testGroup1); - - groupExists = await database.groupDao.groupExists(groupId: testGroup1.id); - expect(groupExists, true); - }); - - test('Deleting a group works correctly', () async { - await database.groupDao.addGroup(group: testGroup1); - - final groupDeleted = await database.groupDao.deleteGroup( - groupId: testGroup1.id, - ); - expect(groupDeleted, true); - - final groupExists = await database.groupDao.groupExists( - groupId: testGroup1.id, - ); - expect(groupExists, false); - }); - - test('Updating a group name works correcly', () async { - await database.groupDao.addGroup(group: testGroup1); - - const newGroupName = 'new group name'; - - await database.groupDao.updateGroupname( - groupId: testGroup1.id, - newName: newGroupName, - ); - - final result = await database.groupDao.getGroupById( - groupId: testGroup1.id, - ); - expect(result.name, newGroupName); - }); - - test('Getting the group count works correctly', () async { - final initialCount = await database.groupDao.getGroupCount(); - expect(initialCount, 0); - - await database.groupDao.addGroup(group: testGroup1); - - final groupAdded = await database.groupDao.getGroupCount(); - expect(groupAdded, 1); - - final groupRemoved = await database.groupDao.deleteGroup( - groupId: testGroup1.id, - ); - expect(groupRemoved, true); - - final finalCount = await database.groupDao.getGroupCount(); - expect(finalCount, 0); - }); - }); -} diff --git a/test/db_tests/player_group_test.dart b/test/db_tests/player_group_test.dart deleted file mode 100644 index b4a87bc..0000000 --- a/test/db_tests/player_group_test.dart +++ /dev/null @@ -1,103 +0,0 @@ -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart'; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/dto/group.dart'; -import 'package:tallee/data/dto/player.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - late Group testgroup; - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testgroup = Group( - name: 'Test Group', - members: [testPlayer1, testPlayer2, testPlayer3], - ); - }); - }); - tearDown(() async { - await database.close(); - }); - - group('Player-Group Tests', () { - /// No need to test if group has players since the members attribute is - /// not nullable - - test('Adding a player to a group works correctly', () async { - await database.groupDao.addGroup(group: testgroup); - await database.playerDao.addPlayer(player: testPlayer4); - await database.playerGroupDao.addPlayerToGroup( - groupId: testgroup.id, - player: testPlayer4, - ); - - var playerAdded = await database.playerGroupDao.isPlayerInGroup( - groupId: testgroup.id, - playerId: testPlayer4.id, - ); - - expect(playerAdded, true); - - playerAdded = await database.playerGroupDao.isPlayerInGroup( - groupId: testgroup.id, - playerId: '', - ); - - expect(playerAdded, false); - }); - - test('Removing player from group works correctly', () async { - await database.groupDao.addGroup(group: testgroup); - - final playerToRemove = testgroup.members[0]; - - final removed = await database.playerGroupDao.removePlayerFromGroup( - playerId: playerToRemove.id, - groupId: testgroup.id, - ); - expect(removed, true); - - final result = await database.groupDao.getGroupById( - groupId: testgroup.id, - ); - expect(result.members.length, testgroup.members.length - 1); - - final playerExists = result.members.any((p) => p.id == playerToRemove.id); - expect(playerExists, false); - }); - - test('Retrieving players of a group works correctly', () async { - await database.groupDao.addGroup(group: testgroup); - final players = await database.playerGroupDao.getPlayersOfGroup( - groupId: testgroup.id, - ); - - for (int i = 0; i < players.length; i++) { - expect(players[i].id, testgroup.members[i].id); - expect(players[i].name, testgroup.members[i].name); - expect(players[i].createdAt, testgroup.members[i].createdAt); - } - }); - }); -} diff --git a/test/db_tests/player_match_test.dart b/test/db_tests/player_match_test.dart deleted file mode 100644 index 393d0c0..0000000 --- a/test/db_tests/player_match_test.dart +++ /dev/null @@ -1,237 +0,0 @@ -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart' hide isNotNull; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/dto/group.dart'; -import 'package:tallee/data/dto/match.dart'; -import 'package:tallee/data/dto/player.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - late Player testPlayer5; - late Player testPlayer6; - late Group testgroup; - late Match testMatchOnlyGroup; - late Match testMatchOnlyPlayers; - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() async { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Alice'); - testPlayer2 = Player(name: 'Bob'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - testPlayer5 = Player(name: 'Eve'); - testPlayer6 = Player(name: 'Frank'); - testgroup = Group( - name: 'Test Group', - members: [testPlayer1, testPlayer2, testPlayer3], - ); - testMatchOnlyGroup = Match( - name: 'Test Match with Group', - group: testgroup, - ); - testMatchOnlyPlayers = Match( - name: 'Test Match with Players', - players: [testPlayer4, testPlayer5, testPlayer6], - ); - }); - await database.playerDao.addPlayersAsList( - players: [ - testPlayer1, - testPlayer2, - testPlayer3, - testPlayer4, - testPlayer5, - testPlayer6, - ], - ); - await database.groupDao.addGroup(group: testgroup); - }); - tearDown(() async { - await database.close(); - }); - - group('Player-Match Tests', () { - test('Match has player works correctly', () async { - await database.matchDao.addMatch(match: testMatchOnlyGroup); - await database.playerDao.addPlayer(player: testPlayer1); - - var matchHasPlayers = await database.playerMatchDao.matchHasPlayers( - matchId: testMatchOnlyGroup.id, - ); - - expect(matchHasPlayers, false); - - await database.playerMatchDao.addPlayerToMatch( - matchId: testMatchOnlyGroup.id, - playerId: testPlayer1.id, - ); - - matchHasPlayers = await database.playerMatchDao.matchHasPlayers( - matchId: testMatchOnlyGroup.id, - ); - - expect(matchHasPlayers, true); - }); - - test('Adding a player to a match works correctly', () async { - await database.matchDao.addMatch(match: testMatchOnlyGroup); - await database.playerDao.addPlayer(player: testPlayer5); - await database.playerMatchDao.addPlayerToMatch( - matchId: testMatchOnlyGroup.id, - playerId: testPlayer5.id, - ); - - var playerAdded = await database.playerMatchDao.isPlayerInMatch( - matchId: testMatchOnlyGroup.id, - playerId: testPlayer5.id, - ); - - expect(playerAdded, true); - - playerAdded = await database.playerMatchDao.isPlayerInMatch( - matchId: testMatchOnlyGroup.id, - playerId: '', - ); - - expect(playerAdded, false); - }); - - test('Removing player from match works correctly', () async { - await database.matchDao.addMatch(match: testMatchOnlyPlayers); - - final playerToRemove = testMatchOnlyPlayers.players![0]; - - final removed = await database.playerMatchDao.removePlayerFromMatch( - playerId: playerToRemove.id, - matchId: testMatchOnlyPlayers.id, - ); - expect(removed, true); - - final result = await database.matchDao.getMatchById( - matchId: testMatchOnlyPlayers.id, - ); - expect(result.players!.length, testMatchOnlyPlayers.players!.length - 1); - - final playerExists = result.players!.any( - (p) => p.id == playerToRemove.id, - ); - expect(playerExists, false); - }); - - test('Retrieving players of a match works correctly', () async { - await database.matchDao.addMatch(match: testMatchOnlyPlayers); - final players = await database.playerMatchDao.getPlayersOfMatch( - matchId: testMatchOnlyPlayers.id, - ); - - if (players == null) { - fail('Players should not be null'); - } - - for (int i = 0; i < players.length; i++) { - expect(players[i].id, testMatchOnlyPlayers.players![i].id); - expect(players[i].name, testMatchOnlyPlayers.players![i].name); - expect( - players[i].createdAt, - testMatchOnlyPlayers.players![i].createdAt, - ); - } - }); - - test('Updating the match players works coreclty', () async { - await database.matchDao.addMatch(match: testMatchOnlyPlayers); - - final newPlayers = [testPlayer1, testPlayer2, testPlayer4]; - await database.playerDao.addPlayersAsList(players: newPlayers); - - // First, remove all existing players - final existingPlayers = await database.playerMatchDao.getPlayersOfMatch( - matchId: testMatchOnlyPlayers.id, - ); - - if (existingPlayers == null || existingPlayers.isEmpty) { - fail('Existing players should not be null or empty'); - } - - await database.playerMatchDao.updatePlayersFromMatch( - matchId: testMatchOnlyPlayers.id, - newPlayer: newPlayers, - ); - - final updatedPlayers = await database.playerMatchDao.getPlayersOfMatch( - matchId: testMatchOnlyPlayers.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); - } - }); - - test( - 'Adding the same player to seperate matches works correctly', - () async { - final playersList = [testPlayer1, testPlayer2, testPlayer3]; - final match1 = Match(name: 'Match 1', players: playersList); - final match2 = Match(name: 'Match 2', players: playersList); - - await Future.wait([ - database.matchDao.addMatch(match: match1), - database.matchDao.addMatch(match: match2), - ]); - - final players1 = await database.playerMatchDao.getPlayersOfMatch( - matchId: match1.id, - ); - final players2 = await database.playerMatchDao.getPlayersOfMatch( - matchId: match2.id, - ); - - expect(players1, isNotNull); - expect(players2, isNotNull); - - expect( - players1!.map((p) => p.id).toList(), - equals(players2!.map((p) => p.id).toList()), - ); - expect( - players1.map((p) => p.name).toList(), - equals(players2.map((p) => p.name).toList()), - ); - expect( - players1.map((p) => p.createdAt).toList(), - equals(players2.map((p) => p.createdAt).toList()), - ); - }, - ); - }); -} diff --git a/test/db_tests/player_test.dart b/test/db_tests/player_test.dart deleted file mode 100644 index c2a4547..0000000 --- a/test/db_tests/player_test.dart +++ /dev/null @@ -1,159 +0,0 @@ -import 'package:clock/clock.dart'; -import 'package:drift/drift.dart'; -import 'package:drift/native.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/dto/player.dart'; - -void main() { - late AppDatabase database; - late Player testPlayer1; - late Player testPlayer2; - late Player testPlayer3; - late Player testPlayer4; - final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); - final fakeClock = Clock(() => fixedDate); - - setUp(() { - database = AppDatabase( - DatabaseConnection( - NativeDatabase.memory(), - // Recommended for widget tests to avoid test errors. - closeStreamsSynchronously: true, - ), - ); - - withClock(fakeClock, () { - testPlayer1 = Player(name: 'Test Player'); - testPlayer2 = Player(name: 'Second Player'); - testPlayer3 = Player(name: 'Charlie'); - testPlayer4 = Player(name: 'Diana'); - }); - }); - tearDown(() async { - await database.close(); - }); - - group('Player Tests', () { - test('Adding and fetching single player works correctly', () async { - await database.playerDao.addPlayer(player: testPlayer1); - await database.playerDao.addPlayer(player: testPlayer2); - - final allPlayers = await database.playerDao.getAllPlayers(); - expect(allPlayers.length, 2); - - final fetchedPlayer1 = allPlayers.firstWhere( - (g) => g.id == testPlayer1.id, - ); - expect(fetchedPlayer1.name, testPlayer1.name); - expect(fetchedPlayer1.createdAt, testPlayer1.createdAt); - - final fetchedPlayer2 = allPlayers.firstWhere( - (g) => g.id == testPlayer2.id, - ); - expect(fetchedPlayer2.name, testPlayer2.name); - expect(fetchedPlayer2.createdAt, testPlayer2.createdAt); - }); - - test('Adding and fetching multiple players works correctly', () async { - await database.playerDao.addPlayersAsList( - players: [testPlayer1, testPlayer2, testPlayer3, testPlayer4], - ); - - final allPlayers = await database.playerDao.getAllPlayers(); - expect(allPlayers.length, 4); - - // Map for connencting fetched players with expected players - final testPlayers = { - testPlayer1.id: testPlayer1, - testPlayer2.id: testPlayer2, - testPlayer3.id: testPlayer3, - testPlayer4.id: testPlayer4, - }; - - for (final player in allPlayers) { - final testPlayer = testPlayers[player.id]!; - - expect(player.id, testPlayer.id); - expect(player.name, testPlayer.name); - expect(player.createdAt, testPlayer.createdAt); - } - }); - - test('Adding the same player twice does not create duplicates', () async { - await database.playerDao.addPlayer(player: testPlayer1); - await database.playerDao.addPlayer(player: testPlayer1); - - final allPlayers = await database.playerDao.getAllPlayers(); - expect(allPlayers.length, 1); - }); - - test('Player existence check works correctly', () async { - var playerExists = await database.playerDao.playerExists( - playerId: testPlayer1.id, - ); - expect(playerExists, false); - - await database.playerDao.addPlayer(player: testPlayer1); - - playerExists = await database.playerDao.playerExists( - playerId: testPlayer1.id, - ); - expect(playerExists, true); - }); - - test('Deleting a player works correctly', () async { - await database.playerDao.addPlayer(player: testPlayer1); - final playerDeleted = await database.playerDao.deletePlayer( - playerId: testPlayer1.id, - ); - expect(playerDeleted, true); - - final playerExists = await database.playerDao.playerExists( - playerId: testPlayer1.id, - ); - expect(playerExists, false); - }); - - test('Updating a player name works correcly', () async { - await database.playerDao.addPlayer(player: testPlayer1); - - const newPlayerName = 'new player name'; - - await database.playerDao.updatePlayername( - playerId: testPlayer1.id, - newName: newPlayerName, - ); - - final result = await database.playerDao.getPlayerById( - playerId: testPlayer1.id, - ); - expect(result.name, newPlayerName); - }); - - test('Getting the player count works correctly', () async { - var playerCount = await database.playerDao.getPlayerCount(); - expect(playerCount, 0); - - await database.playerDao.addPlayer(player: testPlayer1); - - playerCount = await database.playerDao.getPlayerCount(); - expect(playerCount, 1); - - await database.playerDao.addPlayer(player: testPlayer2); - - playerCount = await database.playerDao.getPlayerCount(); - expect(playerCount, 2); - - await database.playerDao.deletePlayer(playerId: testPlayer1.id); - - playerCount = await database.playerDao.getPlayerCount(); - expect(playerCount, 1); - - await database.playerDao.deletePlayer(playerId: testPlayer2.id); - - playerCount = await database.playerDao.getPlayerCount(); - expect(playerCount, 0); - }); - }); -} diff --git a/test/db_tests/relationships/player_group_test.dart b/test/db_tests/relationships/player_group_test.dart new file mode 100644 index 0000000..051daf8 --- /dev/null +++ b/test/db_tests/relationships/player_group_test.dart @@ -0,0 +1,342 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart'; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/group.dart'; +import 'package:tallee/data/dto/player.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Group testGroup; + final fixedDate = DateTime(2025, 19, 11, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + testGroup = Group( + name: 'Test Group', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + }); + }); + tearDown(() async { + await database.close(); + }); + + group('Player-Group Tests', () { + + // Verifies that a player can be added to an existing group and isPlayerInGroup returns true. + test('Adding a player to a group works correctly', () async { + await database.groupDao.addGroup(group: testGroup); + await database.playerDao.addPlayer(player: testPlayer4); + await database.playerGroupDao.addPlayerToGroup( + groupId: testGroup.id, + player: testPlayer4, + ); + + var playerAdded = await database.playerGroupDao.isPlayerInGroup( + groupId: testGroup.id, + playerId: testPlayer4.id, + ); + + expect(playerAdded, true); + + playerAdded = await database.playerGroupDao.isPlayerInGroup( + groupId: testGroup.id, + playerId: '', + ); + + expect(playerAdded, false); + }); + + // Verifies that a player can be removed from a group and the group's member count decreases. + test('Removing player from group works correctly', () async { + await database.groupDao.addGroup(group: testGroup); + + final playerToRemove = testGroup.members[0]; + + final removed = await database.playerGroupDao.removePlayerFromGroup( + playerId: playerToRemove.id, + groupId: testGroup.id, + ); + expect(removed, true); + + final result = await database.groupDao.getGroupById( + groupId: testGroup.id, + ); + expect(result.members.length, testGroup.members.length - 1); + + final playerExists = result.members.any((p) => p.id == playerToRemove.id); + expect(playerExists, false); + }); + + // Verifies that getPlayersOfGroup returns all members of a group with correct data. + test('Retrieving players of a group works correctly', () async { + await database.groupDao.addGroup(group: testGroup); + final players = await database.playerGroupDao.getPlayersOfGroup( + groupId: testGroup.id, + ); + + for (int i = 0; i < players.length; i++) { + expect(players[i].id, testGroup.members[i].id); + expect(players[i].name, testGroup.members[i].name); + expect(players[i].createdAt, testGroup.members[i].createdAt); + } + }); + + // Verifies that isPlayerInGroup returns false for non-existent player. + test('isPlayerInGroup returns false for non-existent player', () async { + await database.groupDao.addGroup(group: testGroup); + + final result = await database.playerGroupDao.isPlayerInGroup( + playerId: 'non-existent-player-id', + groupId: testGroup.id, + ); + + expect(result, false); + }); + + // Verifies that isPlayerInGroup returns false for non-existent group. + test('isPlayerInGroup returns false for non-existent group', () async { + await database.playerDao.addPlayer(player: testPlayer1); + + final result = await database.playerGroupDao.isPlayerInGroup( + playerId: testPlayer1.id, + groupId: 'non-existent-group-id', + ); + + expect(result, false); + }); + + // Verifies that addPlayerToGroup returns false when player already in group. + test('addPlayerToGroup returns false when player already in group', () async { + await database.groupDao.addGroup(group: testGroup); + + // testPlayer1 is already in testGroup via group creation + final result = await database.playerGroupDao.addPlayerToGroup( + player: testPlayer1, + groupId: testGroup.id, + ); + + expect(result, false); + }); + + // Verifies that addPlayerToGroup adds player to player table if not exists. + test('addPlayerToGroup adds player to player table if not exists', () async { + await database.groupDao.addGroup(group: testGroup); + + // testPlayer4 is not in the database yet + var playerExists = await database.playerDao.playerExists( + playerId: testPlayer4.id, + ); + expect(playerExists, false); + + await database.playerGroupDao.addPlayerToGroup( + player: testPlayer4, + groupId: testGroup.id, + ); + + // Now player should exist in player table + playerExists = await database.playerDao.playerExists( + playerId: testPlayer4.id, + ); + expect(playerExists, true); + }); + + // Verifies that removePlayerFromGroup returns false for non-existent player. + test('removePlayerFromGroup returns false for non-existent player', () async { + await database.groupDao.addGroup(group: testGroup); + + final result = await database.playerGroupDao.removePlayerFromGroup( + playerId: 'non-existent-player-id', + groupId: testGroup.id, + ); + + expect(result, false); + }); + + // Verifies that removePlayerFromGroup returns false for non-existent group. + test('removePlayerFromGroup returns false for non-existent group', () async { + await database.playerDao.addPlayer(player: testPlayer1); + + final result = await database.playerGroupDao.removePlayerFromGroup( + playerId: testPlayer1.id, + groupId: 'non-existent-group-id', + ); + + expect(result, false); + }); + + // Verifies that getPlayersOfGroup returns empty list for group with no members. + test('getPlayersOfGroup returns empty list for empty group', () async { + final emptyGroup = Group(name: 'Empty Group', description: '', members: []); + await database.groupDao.addGroup(group: emptyGroup); + + final players = await database.playerGroupDao.getPlayersOfGroup( + groupId: emptyGroup.id, + ); + + expect(players, isEmpty); + }); + + // Verifies that getPlayersOfGroup returns empty list for non-existent group. + test('getPlayersOfGroup returns empty list for non-existent group', () async { + final players = await database.playerGroupDao.getPlayersOfGroup( + groupId: 'non-existent-group-id', + ); + + expect(players, isEmpty); + }); + + // Verifies that removing all players from a group leaves the group empty. + test('Removing all players from a group leaves group empty', () async { + await database.groupDao.addGroup(group: testGroup); + + for (final player in testGroup.members) { + await database.playerGroupDao.removePlayerFromGroup( + playerId: player.id, + groupId: testGroup.id, + ); + } + + final players = await database.playerGroupDao.getPlayersOfGroup( + groupId: testGroup.id, + ); + expect(players, isEmpty); + + // Group should still exist + final groupExists = await database.groupDao.groupExists( + groupId: testGroup.id, + ); + expect(groupExists, true); + }); + + // Verifies that a player can be in multiple groups. + test('Player can be in multiple groups', () async { + final secondGroup = Group(name: 'Second Group', description: '', members: []); + await database.groupDao.addGroup(group: testGroup); + await database.groupDao.addGroup(group: secondGroup); + + // Add testPlayer1 to second group (already in testGroup) + await database.playerGroupDao.addPlayerToGroup( + player: testPlayer1, + groupId: secondGroup.id, + ); + + final inFirstGroup = await database.playerGroupDao.isPlayerInGroup( + playerId: testPlayer1.id, + groupId: testGroup.id, + ); + final inSecondGroup = await database.playerGroupDao.isPlayerInGroup( + playerId: testPlayer1.id, + groupId: secondGroup.id, + ); + + expect(inFirstGroup, true); + expect(inSecondGroup, true); + }); + + // Verifies that removing player from one group doesn't affect other groups. + test('Removing player from one group does not affect other groups', () async { + final secondGroup = Group(name: 'Second Group', description: '', members: [testPlayer1]); + await database.groupDao.addGroup(group: testGroup); + await database.groupDao.addGroup(group: secondGroup); + + // Remove testPlayer1 from testGroup + await database.playerGroupDao.removePlayerFromGroup( + playerId: testPlayer1.id, + groupId: testGroup.id, + ); + + final inFirstGroup = await database.playerGroupDao.isPlayerInGroup( + playerId: testPlayer1.id, + groupId: testGroup.id, + ); + final inSecondGroup = await database.playerGroupDao.isPlayerInGroup( + playerId: testPlayer1.id, + groupId: secondGroup.id, + ); + + expect(inFirstGroup, false); + expect(inSecondGroup, true); + }); + + // Verifies that addPlayerToGroup returns true on successful addition. + test('addPlayerToGroup returns true on successful addition', () async { + await database.groupDao.addGroup(group: testGroup); + await database.playerDao.addPlayer(player: testPlayer4); + + final result = await database.playerGroupDao.addPlayerToGroup( + player: testPlayer4, + groupId: testGroup.id, + ); + + expect(result, true); + }); + + // Verifies that removing the same player twice returns false on second attempt. + test('Removing same player twice returns false on second attempt', () async { + await database.groupDao.addGroup(group: testGroup); + + final firstRemoval = await database.playerGroupDao.removePlayerFromGroup( + playerId: testPlayer1.id, + groupId: testGroup.id, + ); + expect(firstRemoval, true); + + final secondRemoval = await database.playerGroupDao.removePlayerFromGroup( + playerId: testPlayer1.id, + groupId: testGroup.id, + ); + expect(secondRemoval, false); + }); + + // Verifies that replaceGroupPlayers removes all existing players and replaces with new list. + test('replaceGroupPlayers replaces all group members correctly', () async { + // Create initial group with 3 players + await database.groupDao.addGroup(group: testGroup); + + // Verify initial members + var groupMembers = await database.groupDao.getGroupById( + groupId: testGroup.id, + ); + expect(groupMembers.members.length, 3); + + // Replace with new list containing 2 different players + final newPlayersList = [testPlayer3, testPlayer4]; + await database.groupDao.replaceGroupPlayers( + groupId: testGroup.id, + newPlayers: newPlayersList, + ); + + // Get updated group and verify members + groupMembers = await database.groupDao.getGroupById( + groupId: testGroup.id, + ); + + expect(groupMembers.members.length, 2); + expect(groupMembers.members.any((p) => p.id == testPlayer3.id), true); + expect(groupMembers.members.any((p) => p.id == testPlayer4.id), true); + expect(groupMembers.members.any((p) => p.id == testPlayer1.id), false); + expect(groupMembers.members.any((p) => p.id == testPlayer2.id), false); + }); + }); +} diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart new file mode 100644 index 0000000..890e74e --- /dev/null +++ b/test/db_tests/relationships/player_match_test.dart @@ -0,0 +1,915 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNotNull, isNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; +import 'package:tallee/data/dto/group.dart'; +import 'package:tallee/data/dto/match.dart'; +import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/data/dto/team.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Player testPlayer4; + late Player testPlayer5; + late Player testPlayer6; + late Group testGroup; + late Game testGame; + late Match testMatchOnlyGroup; + late Match testMatchOnlyPlayers; + late Team testTeam1; + late Team testTeam2; + final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() async { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testPlayer4 = Player(name: 'Diana', description: ''); + testPlayer5 = Player(name: 'Eve', description: ''); + testPlayer6 = Player(name: 'Frank', description: ''); + testGroup = Group( + name: 'Test Group', + description: '', + members: [testPlayer1, testPlayer2, testPlayer3], + ); + testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: GameColor.blue, icon: ''); + testMatchOnlyGroup = Match( + name: 'Test Match with Group', + game: testGame, + group: testGroup, + notes: '', + ); + testMatchOnlyPlayers = Match( + name: 'Test Match with Players', + game: testGame, + players: [testPlayer4, testPlayer5, testPlayer6], + notes: '', + ); + testTeam1 = Team( + name: 'Team Alpha', + members: [testPlayer1, testPlayer2], + ); + testTeam2 = Team( + name: 'Team Beta', + members: [testPlayer3, testPlayer4], + ); + }); + await database.playerDao.addPlayersAsList( + players: [ + testPlayer1, + testPlayer2, + testPlayer3, + testPlayer4, + testPlayer5, + testPlayer6, + ], + ); + await database.groupDao.addGroup(group: testGroup); + await database.gameDao.addGame(game: testGame); + }); + tearDown(() async { + await database.close(); + }); + + group('Player-Match Tests', () { + + // Verifies that matchHasPlayers returns false initially and true after adding a player. + test('Match has player works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.playerDao.addPlayer(player: testPlayer1); + + var matchHasPlayers = await database.playerMatchDao.matchHasPlayers( + matchId: testMatchOnlyGroup.id, + ); + + expect(matchHasPlayers, false); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + + matchHasPlayers = await database.playerMatchDao.matchHasPlayers( + matchId: testMatchOnlyGroup.id, + ); + + expect(matchHasPlayers, true); + }); + + // Verifies that a player can be added to a match and isPlayerInMatch returns true. + test('Adding a player to a match works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.playerDao.addPlayer(player: testPlayer5); + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer5.id, + ); + + var playerAdded = await database.playerMatchDao.isPlayerInMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer5.id, + ); + + expect(playerAdded, true); + + playerAdded = await database.playerMatchDao.isPlayerInMatch( + matchId: testMatchOnlyGroup.id, + playerId: '', + ); + + expect(playerAdded, false); + }); + + // Verifies that a player can be removed from a match and the player count decreases. + test('Removing player from match works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final playerToRemove = testMatchOnlyPlayers.players[0]; + + final removed = await database.playerMatchDao.removePlayerFromMatch( + playerId: playerToRemove.id, + matchId: testMatchOnlyPlayers.id, + ); + expect(removed, true); + + final result = await database.matchDao.getMatchById( + matchId: testMatchOnlyPlayers.id, + ); + expect(result.players.length, testMatchOnlyPlayers.players.length - 1); + + final playerExists = result.players.any( + (p) => p.id == playerToRemove.id, + ); + expect(playerExists, false); + }); + + // Verifies that getPlayersOfMatch returns all players of a match with correct data. + test('Retrieving players of a match works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + final players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.id, + ) ?? []; + + for (int i = 0; i < players.length; i++) { + expect(players[i].id, testMatchOnlyPlayers.players[i].id); + expect(players[i].name, testMatchOnlyPlayers.players[i].name); + expect( + players[i].createdAt, + testMatchOnlyPlayers.players[i].createdAt, + ); + } + }); + + // Verifies that updatePlayersFromMatch replaces all existing players with new ones. + test('Updating the match players works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final newPlayers = [testPlayer1, testPlayer2, testPlayer4]; + await database.playerDao.addPlayersAsList(players: newPlayers); + + // First, remove all existing players + final existingPlayers = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.id, + ); + + if (existingPlayers == null || existingPlayers.isEmpty) { + fail('Existing players should not be null or empty'); + } + + await database.playerMatchDao.updatePlayersFromMatch( + matchId: testMatchOnlyPlayers.id, + newPlayer: newPlayers, + ); + + final updatedPlayers = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.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); + } + }); + + // Verifies that the same player can be added to multiple different matches. + test( + 'Adding the same player to separate matches works correctly', + () async { + final playersList = [testPlayer1, testPlayer2, testPlayer3]; + final match1 = Match(name: 'Match 1', game: testGame, players: playersList, notes: ''); + final match2 = Match(name: 'Match 2', game: testGame, players: playersList, notes: ''); + + await Future.wait([ + database.matchDao.addMatch(match: match1), + database.matchDao.addMatch(match: match2), + ]); + + final players1 = await database.playerMatchDao.getPlayersOfMatch( + matchId: match1.id, + ); + final players2 = await database.playerMatchDao.getPlayersOfMatch( + matchId: match2.id, + ); + + expect(players1, isNotNull); + expect(players2, isNotNull); + + expect( + players1!.map((p) => p.id).toList(), + equals(players2!.map((p) => p.id).toList()), + ); + expect( + players1.map((p) => p.name).toList(), + equals(players2.map((p) => p.name).toList()), + ); + expect( + players1.map((p) => p.createdAt).toList(), + equals(players2.map((p) => p.createdAt).toList()), + ); + }, + ); + + // Verifies that getPlayersOfMatch returns null for a non-existent match. + test('getPlayersOfMatch returns null for non-existent match', () async { + final players = await database.playerMatchDao.getPlayersOfMatch( + matchId: 'non-existent-match-id', + ); + + expect(players, isNull); + }); + + // Verifies that adding a player with initial score works correctly. + test('Adding player with initial score works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + score: 100, + ); + + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + + expect(score, 100); + }); + + // Verifies that getPlayerScore returns the correct score. + test('getPlayerScore returns correct score', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + // Default score should be 0 when added through match + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + ); + + expect(score, 0); + }); + + // Verifies that getPlayerScore returns null for non-existent player-match combination. + test('getPlayerScore returns null for non-existent player in match', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyGroup.id, + playerId: 'non-existent-player-id', + ); + + expect(score, isNull); + }); + + // Verifies that updatePlayerScore updates the score correctly. + test('updatePlayerScore updates score correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final updated = await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + newScore: 50, + ); + + expect(updated, true); + + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + ); + + expect(score, 50); + }); + + // Verifies that updatePlayerScore returns false for non-existent player-match. + test('updatePlayerScore returns false for non-existent player-match', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + + final updated = await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyGroup.id, + playerId: 'non-existent-player-id', + newScore: 50, + ); + + expect(updated, false); + }); + + // Verifies that adding a player with teamId works correctly. + test('Adding player with teamId works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + ); + + final playersInTeam = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + + expect(playersInTeam.length, 1); + expect(playersInTeam[0].id, testPlayer1.id); + }); + + // Verifies that updatePlayerTeam updates the team correctly. + test('updatePlayerTeam updates team correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + await database.teamDao.addTeam(team: testTeam2); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + ); + + // Update player's team + final updated = await database.playerMatchDao.updatePlayerTeam( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam2.id, + ); + + expect(updated, true); + + // Verify player is now in testTeam2 + final playersInTeam2 = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam2.id, + ); + + expect(playersInTeam2.length, 1); + expect(playersInTeam2[0].id, testPlayer1.id); + + // Verify player is no longer in testTeam1 + final playersInTeam1 = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + + expect(playersInTeam1.isEmpty, true); + }); + + // Verifies that updatePlayerTeam can set team to null. + test('updatePlayerTeam can remove player from team', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + ); + + // Remove player from team by setting teamId to null + final updated = await database.playerMatchDao.updatePlayerTeam( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: null, + ); + + expect(updated, true); + + final playersInTeam = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + + expect(playersInTeam.isEmpty, true); + }); + + // Verifies that updatePlayerTeam returns false for non-existent player-match. + test('updatePlayerTeam returns false for non-existent player-match', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + + final updated = await database.playerMatchDao.updatePlayerTeam( + matchId: testMatchOnlyGroup.id, + playerId: 'non-existent-player-id', + teamId: testTeam1.id, + ); + + expect(updated, false); + }); + + // Verifies that getPlayersInTeam returns empty list for non-existent team. + test('getPlayersInTeam returns empty list for non-existent team', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final players = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyPlayers.id, + teamId: 'non-existent-team-id', + ); + + expect(players.isEmpty, true); + }); + + // Verifies that getPlayersInTeam returns all players of a team. + test('getPlayersInTeam returns all players of a team', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + ); + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer2.id, + teamId: testTeam1.id, + ); + + final playersInTeam = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + + expect(playersInTeam.length, 2); + final playerIds = playersInTeam.map((p) => p.id).toSet(); + expect(playerIds.contains(testPlayer1.id), true); + expect(playerIds.contains(testPlayer2.id), true); + }); + + // Verifies that removePlayerFromMatch returns false for non-existent player. + test('removePlayerFromMatch returns false for non-existent player', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final removed = await database.playerMatchDao.removePlayerFromMatch( + playerId: 'non-existent-player-id', + matchId: testMatchOnlyPlayers.id, + ); + + expect(removed, false); + }); + + // Verifies that adding the same player twice to the same match is ignored. + test('Adding same player twice to same match is ignored', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + score: 10, + ); + + // Try to add the same player again with different score + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + score: 100, + ); + + // Score should still be 10 because insert was ignored + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + + expect(score, 10); + + // Verify player count is still 1 + final players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyGroup.id, + ); + + expect(players?.length, 1); + }); + + // Verifies that updatePlayersFromMatch with empty list removes all players. + test('updatePlayersFromMatch with empty list removes all players', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + // Verify players exist initially + var players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.id, + ); + expect(players?.length, 3); + + // Update with empty list + await database.playerMatchDao.updatePlayersFromMatch( + matchId: testMatchOnlyPlayers.id, + newPlayer: [], + ); + + // Verify all players are removed + players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.id, + ); + expect(players, isNull); + }); + + // Verifies that updatePlayersFromMatch with same players makes no changes. + test('updatePlayersFromMatch with same players makes no changes', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final originalPlayers = [testPlayer4, testPlayer5, testPlayer6]; + + await database.playerMatchDao.updatePlayersFromMatch( + matchId: testMatchOnlyPlayers.id, + newPlayer: originalPlayers, + ); + + final players = await database.playerMatchDao.getPlayersOfMatch( + matchId: testMatchOnlyPlayers.id, + ); + + expect(players?.length, originalPlayers.length); + final playerIds = players!.map((p) => p.id).toSet(); + for (final originalPlayer in originalPlayers) { + expect(playerIds.contains(originalPlayer.id), true); + } + }); + + // Verifies that matchHasPlayers returns false for non-existent match. + test('matchHasPlayers returns false for non-existent match', () async { + final hasPlayers = await database.playerMatchDao.matchHasPlayers( + matchId: 'non-existent-match-id', + ); + + expect(hasPlayers, false); + }); + + // Verifies that isPlayerInMatch returns false for non-existent match. + test('isPlayerInMatch returns false for non-existent match', () async { + final isInMatch = await database.playerMatchDao.isPlayerInMatch( + matchId: 'non-existent-match-id', + playerId: testPlayer1.id, + ); + + expect(isInMatch, false); + }); + + // Verifies that updatePlayersFromMatch preserves scores for existing players. + test('updatePlayersFromMatch only modifies player associations', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + // Update score for existing player + await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + newScore: 75, + ); + + // Update players, keeping testPlayer4 and adding testPlayer1 + await database.playerMatchDao.updatePlayersFromMatch( + matchId: testMatchOnlyPlayers.id, + newPlayer: [testPlayer4, testPlayer1], + ); + + // Verify testPlayer4's score is preserved + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + ); + + expect(score, 75); + + // Verify testPlayer1 was added with default score + final newPlayerScore = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer1.id, + ); + + expect(newPlayerScore, 0); + }); + + // Verifies that adding a player with both score and teamId works correctly. + test('Adding player with score and teamId works correctly', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + score: 150, + ); + + // Verify score + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + expect(score, 150); + + // Verify team assignment + final playersInTeam = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + expect(playersInTeam.length, 1); + expect(playersInTeam[0].id, testPlayer1.id); + }); + + // Verifies that updating score with negative value works. + test('updatePlayerScore with negative score works', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + final updated = await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + newScore: -10, + ); + + expect(updated, true); + + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + ); + + expect(score, -10); + }); + + // Verifies that updating score with zero value works. + test('updatePlayerScore with zero score works', () async { + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + // First set a non-zero score + await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + newScore: 100, + ); + + // Then update to zero + final updated = await database.playerMatchDao.updatePlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + newScore: 0, + ); + + expect(updated, true); + + final score = await database.playerMatchDao.getPlayerScore( + matchId: testMatchOnlyPlayers.id, + playerId: testPlayer4.id, + ); + + expect(score, 0); + }); + + // Verifies that getPlayersInTeam returns empty list for non-existent match. + test('getPlayersInTeam returns empty list for non-existent match', () async { + await database.teamDao.addTeam(team: testTeam1); + + final players = await database.playerMatchDao.getPlayersInTeam( + matchId: 'non-existent-match-id', + teamId: testTeam1.id, + ); + + expect(players.isEmpty, true); + }); + + // Verifies that players in different teams within the same match are returned correctly. + test('Players in different teams within same match are separate', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + await database.teamDao.addTeam(team: testTeam2); + + // Add players to different teams + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + teamId: testTeam1.id, + ); + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer2.id, + teamId: testTeam1.id, + ); + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer3.id, + teamId: testTeam2.id, + ); + + // Verify team 1 players + final playersInTeam1 = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + expect(playersInTeam1.length, 2); + final team1Ids = playersInTeam1.map((p) => p.id).toSet(); + expect(team1Ids.contains(testPlayer1.id), true); + expect(team1Ids.contains(testPlayer2.id), true); + expect(team1Ids.contains(testPlayer3.id), false); + + // Verify team 2 players + final playersInTeam2 = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam2.id, + ); + expect(playersInTeam2.length, 1); + expect(playersInTeam2[0].id, testPlayer3.id); + }); + + // Verifies that removePlayerFromMatch does not affect other matches. + test('removePlayerFromMatch does not affect other matches', () async { + final playersList = [testPlayer1, testPlayer2]; + final match1 = Match(name: 'Match 1', game: testGame, players: playersList, notes: ''); + final match2 = Match(name: 'Match 2', game: testGame, players: playersList, notes: ''); + + await Future.wait([ + database.matchDao.addMatch(match: match1), + database.matchDao.addMatch(match: match2), + ]); + + // Remove player from match1 + final removed = await database.playerMatchDao.removePlayerFromMatch( + playerId: testPlayer1.id, + matchId: match1.id, + ); + expect(removed, true); + + // Verify player is removed from match1 + final isInMatch1 = await database.playerMatchDao.isPlayerInMatch( + matchId: match1.id, + playerId: testPlayer1.id, + ); + expect(isInMatch1, false); + + // Verify player still exists in match2 + final isInMatch2 = await database.playerMatchDao.isPlayerInMatch( + matchId: match2.id, + playerId: testPlayer1.id, + ); + expect(isInMatch2, true); + }); + + // Verifies that updating scores for players in different matches are independent. + test('Player scores are independent across matches', () async { + final playersList = [testPlayer1]; + final match1 = Match(name: 'Match 1', game: testGame, players: playersList, notes: ''); + final match2 = Match(name: 'Match 2', game: testGame, players: playersList, notes: ''); + + await Future.wait([ + database.matchDao.addMatch(match: match1), + database.matchDao.addMatch(match: match2), + ]); + + // Update score in match1 + await database.playerMatchDao.updatePlayerScore( + matchId: match1.id, + playerId: testPlayer1.id, + newScore: 100, + ); + + // Update score in match2 + await database.playerMatchDao.updatePlayerScore( + matchId: match2.id, + playerId: testPlayer1.id, + newScore: 50, + ); + + // Verify scores are independent + final scoreInMatch1 = await database.playerMatchDao.getPlayerScore( + matchId: match1.id, + playerId: testPlayer1.id, + ); + final scoreInMatch2 = await database.playerMatchDao.getPlayerScore( + matchId: match2.id, + playerId: testPlayer1.id, + ); + + expect(scoreInMatch1, 100); + expect(scoreInMatch2, 50); + }); + + // Verifies that updatePlayersFromMatch on non-existent match fails with constraint error. + test('updatePlayersFromMatch on non-existent match fails with foreign key constraint', () async { + // Should throw due to foreign key constraint - match doesn't exist + await expectLater( + database.playerMatchDao.updatePlayersFromMatch( + matchId: 'non-existent-match-id', + newPlayer: [testPlayer1, testPlayer2], + ), + throwsA(anything), + ); + }); + + // Verifies that a player can be in a match without being assigned to a team. + test('Player can exist in match without team assignment', () async { + await database.matchDao.addMatch(match: testMatchOnlyGroup); + await database.teamDao.addTeam(team: testTeam1); + + // Add player to match without team + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + + // Add another player to match with team + await database.playerMatchDao.addPlayerToMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer2.id, + teamId: testTeam1.id, + ); + + // Verify both players are in the match + final isPlayer1InMatch = await database.playerMatchDao.isPlayerInMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer1.id, + ); + final isPlayer2InMatch = await database.playerMatchDao.isPlayerInMatch( + matchId: testMatchOnlyGroup.id, + playerId: testPlayer2.id, + ); + + expect(isPlayer1InMatch, true); + expect(isPlayer2InMatch, true); + + // Verify only player2 is in the team + final playersInTeam = await database.playerMatchDao.getPlayersInTeam( + matchId: testMatchOnlyGroup.id, + teamId: testTeam1.id, + ); + + expect(playersInTeam.length, 1); + expect(playersInTeam[0].id, testPlayer2.id); + }); + + // Verifies that replaceMatchPlayers removes all existing players and replaces with new list. + test('replaceMatchPlayers replaces all match players correctly', () async { + // Create initial match with 3 players + await database.matchDao.addMatch(match: testMatchOnlyPlayers); + + // Verify initial players + var matchPlayers = await database.matchDao.getMatchById( + matchId: testMatchOnlyPlayers.id, + ); + expect(matchPlayers.players.length, 3); + + // Replace with new list containing 2 different players + final newPlayersList = [testPlayer1, testPlayer2]; + await database.matchDao.replaceMatchPlayers( + matchId: testMatchOnlyPlayers.id, + newPlayers: newPlayersList, + ); + + // Get updated match and verify players + matchPlayers = await database.matchDao.getMatchById( + matchId: testMatchOnlyPlayers.id, + ); + + expect(matchPlayers.players.length, 2); + expect(matchPlayers.players.any((p) => p.id == testPlayer1.id), true); + expect(matchPlayers.players.any((p) => p.id == testPlayer2.id), true); + expect(matchPlayers.players.any((p) => p.id == testPlayer4.id), false); + expect(matchPlayers.players.any((p) => p.id == testPlayer5.id), false); + expect(matchPlayers.players.any((p) => p.id == testPlayer6.id), false); + }); + }); +} diff --git a/test/db_tests/values/score_test.dart b/test/db_tests/values/score_test.dart new file mode 100644 index 0000000..2ad4671 --- /dev/null +++ b/test/db_tests/values/score_test.dart @@ -0,0 +1,739 @@ +import 'package:clock/clock.dart'; +import 'package:drift/drift.dart' hide isNull, isNotNull; +import 'package:drift/native.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/dto/game.dart'; +import 'package:tallee/data/dto/match.dart'; +import 'package:tallee/data/dto/player.dart'; +import 'package:tallee/core/enums.dart'; + +void main() { + late AppDatabase database; + late Player testPlayer1; + late Player testPlayer2; + late Player testPlayer3; + late Game testGame; + late Match testMatch1; + late Match testMatch2; + final fixedDate = DateTime(2025, 11, 19, 00, 11, 23); + final fakeClock = Clock(() => fixedDate); + + setUp(() async { + database = AppDatabase( + DatabaseConnection( + NativeDatabase.memory(), + // Recommended for widget tests to avoid test errors. + closeStreamsSynchronously: true, + ), + ); + + withClock(fakeClock, () { + testPlayer1 = Player(name: 'Alice', description: ''); + testPlayer2 = Player(name: 'Bob', description: ''); + testPlayer3 = Player(name: 'Charlie', description: ''); + testGame = Game(name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', color: GameColor.blue, icon: ''); + testMatch1 = Match( + name: 'Test Match 1', + game: testGame, + players: [testPlayer1, testPlayer2], + notes: '', + ); + testMatch2 = Match( + name: 'Test Match 2', + game: testGame, + players: [testPlayer2, testPlayer3], + notes: '', + ); + }); + + await database.playerDao.addPlayersAsList( + players: [testPlayer1, testPlayer2, testPlayer3], + ); + await database.gameDao.addGame(game: testGame); + await database.matchDao.addMatch(match: testMatch1); + await database.matchDao.addMatch(match: testMatch2); + }); + + tearDown(() async { + await database.close(); + }); + + group('Score Tests', () { + + // Verifies that a score can be added and retrieved with all fields intact. + test('Adding and fetching a score works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNotNull); + expect(score!.playerId, testPlayer1.id); + expect(score.matchId, testMatch1.id); + expect(score.roundNumber, 1); + expect(score.score, 10); + expect(score.change, 10); + }); + + // Verifies that getScoresForMatch returns all scores for a given match. + test('Getting scores for a match works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 2, + score: 25, + change: 15, + ); + + final scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch1.id, + ); + + expect(scores.length, 3); + }); + + // Verifies that getPlayerScoresInMatch returns all scores for a player in a match, ordered by round. + test('Getting player scores in a match works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 2, + score: 25, + change: 15, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 3, + score: 30, + change: 5, + ); + + final playerScores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + + expect(playerScores.length, 3); + expect(playerScores[0].roundNumber, 1); + expect(playerScores[1].roundNumber, 2); + expect(playerScores[2].roundNumber, 3); + expect(playerScores[0].score, 10); + expect(playerScores[1].score, 25); + expect(playerScores[2].score, 30); + }); + + // Verifies that getScoreForRound returns null for a non-existent round number. + test('Getting score for a non-existent round returns null', () async { + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 999, + ); + + expect(score, isNull); + }); + + // Verifies that updateScore correctly updates the score and change values. + test('Updating a score works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + + final updated = await database.scoreDao.updateScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + newScore: 50, + newChange: 40, + ); + + expect(updated, true); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNotNull); + expect(score!.score, 50); + expect(score.change, 40); + }); + + // Verifies that updateScore returns false for a non-existent score entry. + test('Updating a non-existent score returns false', () async { + final updated = await database.scoreDao.updateScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 999, + newScore: 50, + newChange: 40, + ); + + expect(updated, false); + }); + + // Verifies that deleteScore removes the score entry and returns true. + test('Deleting a score works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + + final deleted = await database.scoreDao.deleteScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(deleted, true); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNull); + }); + + // Verifies that deleteScore returns false for a non-existent score entry. + test('Deleting a non-existent score returns false', () async { + final deleted = await database.scoreDao.deleteScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 999, + ); + + expect(deleted, false); + }); + + // Verifies that deleteScoresForMatch removes all scores for a match but keeps other match scores. + test('Deleting scores for a match works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch2.id, + roundNumber: 1, + score: 15, + change: 15, + ); + + final deleted = await database.scoreDao.deleteScoresForMatch( + matchId: testMatch1.id, + ); + + expect(deleted, true); + + final match1Scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch1.id, + ); + expect(match1Scores.length, 0); + + final match2Scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch2.id, + ); + expect(match2Scores.length, 1); + }); + + // Verifies that deleteScoresForPlayer removes all scores for a player across all matches. + test('Deleting scores for a player works correctly', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch2.id, + roundNumber: 1, + score: 15, + change: 15, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + + final deleted = await database.scoreDao.deleteScoresForPlayer( + playerId: testPlayer1.id, + ); + + expect(deleted, true); + + final player1Scores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + expect(player1Scores.length, 0); + + final player2Scores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer2.id, + matchId: testMatch1.id, + ); + expect(player2Scores.length, 1); + }); + + // Verifies that getLatestRoundNumber returns the highest round number for a match. + test('Getting latest round number works correctly', () async { + var latestRound = await database.scoreDao.getLatestRoundNumber( + matchId: testMatch1.id, + ); + expect(latestRound, 0); + + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + + latestRound = await database.scoreDao.getLatestRoundNumber( + matchId: testMatch1.id, + ); + expect(latestRound, 1); + + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 5, + score: 50, + change: 40, + ); + + latestRound = await database.scoreDao.getLatestRoundNumber( + matchId: testMatch1.id, + ); + expect(latestRound, 5); + }); + + // Verifies that getTotalScoreForPlayer returns the latest score (cumulative) for a player. + test('Getting total score for a player works correctly', () async { + var totalScore = await database.scoreDao.getTotalScoreForPlayer( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + expect(totalScore, 0); + + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 2, + score: 25, + change: 15, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 3, + score: 40, + change: 15, + ); + + totalScore = await database.scoreDao.getTotalScoreForPlayer( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + expect(totalScore, 40); + }); + + // Verifies that adding a score with the same player/match/round replaces the existing one. + test('Adding the same score twice replaces the existing one', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 99, + change: 99, + ); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNotNull); + expect(score!.score, 99); + expect(score.change, 99); + }); + + // Verifies that getScoresForMatch returns empty list for match with no scores. + test('Getting scores for match with no scores returns empty list', () async { + final scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch1.id, + ); + + expect(scores.isEmpty, true); + }); + + // Verifies that getPlayerScoresInMatch returns empty list when player has no scores. + test('Getting player scores with no scores returns empty list', () async { + final playerScores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + + expect(playerScores.isEmpty, true); + }); + + // Verifies that scores can have negative values. + test('Score can have negative values', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: -10, + change: -10, + ); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNotNull); + expect(score!.score, -10); + expect(score.change, -10); + }); + + // Verifies that scores can have zero values. + test('Score can have zero values', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 0, + change: 0, + ); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(score, isNotNull); + expect(score!.score, 0); + expect(score.change, 0); + }); + + // Verifies that very large round numbers are supported. + test('Score supports very large round numbers', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 999999, + score: 100, + change: 100, + ); + + final score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 999999, + ); + + expect(score, isNotNull); + expect(score!.roundNumber, 999999); + }); + + // Verifies that getLatestRoundNumber returns max correctly for non-consecutive rounds. + test('Getting latest round number with non-consecutive rounds', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 5, + score: 50, + change: 40, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 3, + score: 30, + change: 20, + ); + + final latestRound = await database.scoreDao.getLatestRoundNumber( + matchId: testMatch1.id, + ); + + expect(latestRound, 5); + }); + + // Verifies that deleteScoresForMatch returns false when no scores exist. + test('Deleting scores for empty match returns false', () async { + final deleted = await database.scoreDao.deleteScoresForMatch( + matchId: testMatch1.id, + ); + + expect(deleted, false); + }); + + // Verifies that deleteScoresForPlayer returns false when player has no scores. + test('Deleting scores for player with no scores returns false', () async { + final deleted = await database.scoreDao.deleteScoresForPlayer( + playerId: testPlayer1.id, + ); + + expect(deleted, false); + }); + + // Verifies that multiple players in same match can have independent score updates. + test('Multiple players in same match have independent scores', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + + await database.scoreDao.updateScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + newScore: 100, + newChange: 90, + ); + + final player1Score = await database.scoreDao.getScoreForRound( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + final player2Score = await database.scoreDao.getScoreForRound( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + ); + + expect(player1Score!.score, 100); + expect(player2Score!.score, 20); + }); + + // Verifies that scores are isolated across different matches. + test('Scores are isolated across different matches', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch2.id, + roundNumber: 1, + score: 50, + change: 50, + ); + + final match1Scores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + final match2Scores = await database.scoreDao.getPlayerScoresInMatch( + playerId: testPlayer1.id, + matchId: testMatch2.id, + ); + + expect(match1Scores.length, 1); + expect(match2Scores.length, 1); + expect(match1Scores[0].score, 10); + expect(match2Scores[0].score, 50); + }); + + // Verifies that getTotalScoreForPlayer returns latest score across multiple rounds. + test('Total score for player returns latest cumulative score', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 2, + score: 25, + change: 25, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 3, + score: 50, + change: 25, + ); + + final totalScore = await database.scoreDao.getTotalScoreForPlayer( + playerId: testPlayer1.id, + matchId: testMatch1.id, + ); + + // Should return the highest round's score + expect(totalScore, 50); + }); + + // Verifies that updating one player's score doesn't affect another player's score in same round. + test('Updating one player score does not affect other players in same round', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + await database.scoreDao.addScore( + playerId: testPlayer3.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 30, + change: 30, + ); + + await database.scoreDao.updateScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + newScore: 99, + newChange: 89, + ); + + final scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch1.id, + ); + + expect(scores.length, 3); + expect(scores.where((s) => s.playerId == testPlayer1.id).first.score, 10); + expect(scores.where((s) => s.playerId == testPlayer2.id).first.score, 99); + expect(scores.where((s) => s.playerId == testPlayer3.id).first.score, 30); + }); + + // Verifies that deleting a player's scores only affects that specific player. + test('Deleting player scores only affects target player', () async { + await database.scoreDao.addScore( + playerId: testPlayer1.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 10, + change: 10, + ); + await database.scoreDao.addScore( + playerId: testPlayer2.id, + matchId: testMatch1.id, + roundNumber: 1, + score: 20, + change: 20, + ); + + await database.scoreDao.deleteScoresForPlayer( + playerId: testPlayer1.id, + ); + + final match1Scores = await database.scoreDao.getScoresForMatch( + matchId: testMatch1.id, + ); + + expect(match1Scores.length, 1); + expect(match1Scores[0].playerId, testPlayer2.id); + }); + }); +}