diff --git a/.gitea/workflows/pull_request.yaml b/.gitea/workflows/pull_request.yaml index 29c6799..78d3269 100644 --- a/.gitea/workflows/pull_request.yaml +++ b/.gitea/workflows/pull_request.yaml @@ -31,20 +31,16 @@ jobs: test: runs-on: ubuntu-latest + container: + image: ghcr.io/cirruslabs/flutter:stable steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Required for Flutter action - - name: Install jq + - name: Install Node run: | apt-get update - apt-get install -y jq + apt-get install -y nodejs npm - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable + - name: Checkout code + uses: actions/checkout@v4 - name: Get dependencies run: | diff --git a/.gitea/workflows/push.yaml b/.gitea/workflows/push.yaml index e836082..a006e74 100644 --- a/.gitea/workflows/push.yaml +++ b/.gitea/workflows/push.yaml @@ -7,22 +7,19 @@ on: - "main" jobs: + test: runs-on: ubuntu-latest + container: + image: ghcr.io/cirruslabs/flutter:stable steps: - - name: Checkout code - uses: actions/checkout@v4 - - # Required for Flutter action - - name: Install jq + - name: Install Node run: | apt-get update - apt-get install -y jq + apt-get install -y nodejs npm - - name: Set up Flutter - uses: subosito/flutter-action@v2 - with: - channel: stable + - name: Checkout code + uses: actions/checkout@v4 - name: Get dependencies run: | @@ -295,6 +292,8 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v3 + with: + packages: "platform-tools platforms;android-34 build-tools;34.0.0" # Required for Flutter action - name: Install jq diff --git a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 7442998..4d7193e 100644 --- a/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,13 +1,5 @@ { "pins" : [ - { - "identity" : "csqlite", - "kind" : "remoteSourceControl", - "location" : "https://github.com/simolus3/CSQLite.git", - "state" : { - "revision" : "1ee46d19a4f451a7aa64ffc64fc99b4748131e62" - } - }, { "identity" : "dkcamera", "kind" : "remoteSourceControl", diff --git a/lib/core/common.dart b/lib/core/common.dart index 312e3fa..df88ea3 100644 --- a/lib/core/common.dart +++ b/lib/core/common.dart @@ -24,48 +24,48 @@ String translateRulesetToString(Ruleset ruleset, BuildContext context) { } } -/// Translates a [GameColor] enum value to its corresponding localized string. -String translateGameColorToString(GameColor color, BuildContext context) { +/// Translates a [AppColor] enum value to its corresponding localized string. +String translateAppColorToString(AppColor color, BuildContext context) { final loc = AppLocalizations.of(context); switch (color) { - case GameColor.red: + case AppColor.red: return loc.color_red; - case GameColor.blue: + case AppColor.blue: return loc.color_blue; - case GameColor.green: + case AppColor.green: return loc.color_green; - case GameColor.yellow: + case AppColor.yellow: return loc.color_yellow; - case GameColor.purple: + case AppColor.purple: return loc.color_purple; - case GameColor.orange: + case AppColor.orange: return loc.color_orange; - case GameColor.pink: + case AppColor.pink: return loc.color_pink; - case GameColor.teal: + case AppColor.teal: return loc.color_teal; } } -/// Returns the [Color] object corresponding to a [GameColor] enum value. -Color getColorFromGameColor(GameColor color) { +/// Returns the [Color] object corresponding to a [AppColor] enum value. +Color getColorFromAppColor(AppColor color) { switch (color) { - case GameColor.red: + case AppColor.red: return Colors.red; - case GameColor.blue: + case AppColor.blue: return Colors.blue; - case GameColor.green: + case AppColor.green: return Colors.green; - case GameColor.yellow: + case AppColor.yellow: return const Color(0xFFF7CA28); - case GameColor.purple: + case AppColor.purple: return Colors.purple; - case GameColor.orange: + case AppColor.orange: return const Color(0xFFef681f); - case GameColor.pink: - return Colors.pink; - case GameColor.teal: - return Colors.teal; + case AppColor.pink: + return const Color(0xFFE91E63); + case AppColor.teal: + return const Color(0xFF00BCD4); } } diff --git a/lib/core/enums.dart b/lib/core/enums.dart index 99141e4..073fd7a 100644 --- a/lib/core/enums.dart +++ b/lib/core/enums.dart @@ -43,4 +43,32 @@ enum Ruleset { } /// Different colors for highlighting games -enum GameColor { red, orange, yellow, green, teal, blue, purple, pink } +enum AppColor { red, orange, yellow, green, teal, blue, purple, pink } + +enum StatisticType { + totalMatches, + totalWins, + totalScore, + totalLosses, + averageScore, + bestScore, + worstScore, + winrate, +} + +enum StatisticScope { + allPlayers, + //selectedPlayer, + selectedGroups, + selectedGames, + timeframe, +} + +enum Timeframe { + last7Days, + last30Days, + last90Days, + last180Days, + lastYear, + allTime, +} diff --git a/lib/data/dao/game_dao.dart b/lib/data/dao/game_dao.dart index a4c2300..3ee8ebd 100644 --- a/lib/data/dao/game_dao.dart +++ b/lib/data/dao/game_dao.dart @@ -77,8 +77,8 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// 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; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all games from the database. @@ -92,7 +92,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { name: row.name, ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), description: row.description, - color: GameColor.values.firstWhere((e) => e.name == row.color), + color: AppColor.values.firstWhere((e) => e.name == row.color), icon: row.icon, createdAt: row.createdAt, ), @@ -103,15 +103,15 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// 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(); + final row = 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, + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, ); } @@ -123,7 +123,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String name, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -135,7 +135,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required Ruleset ruleset, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(ruleset: Value(ruleset.name)), ); return rowsAffected > 0; @@ -147,7 +147,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String description, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(description: Value(description)), ); return rowsAffected > 0; @@ -156,10 +156,10 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// Updates the color of the game with the given [gameId]. Future updateGameColor({ required String gameId, - required GameColor color, + required AppColor color, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(color: Value(color.name)), ); return rowsAffected > 0; @@ -171,7 +171,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { required String icon, }) async { final rowsAffected = - await (update(gameTable)..where((g) => g.id.equals(gameId))).write( + await (update(gameTable)..where((tbl) => tbl.id.equals(gameId))).write( GameTableCompanion(icon: Value(icon)), ); return rowsAffected > 0; @@ -182,7 +182,7 @@ class GameDao extends DatabaseAccessor with _$GameDaoMixin { /// 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 query = delete(gameTable)..where((tbl) => tbl.id.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/group_dao.dart b/lib/data/dao/group_dao.dart index 2de2ab9..8d1c0a2 100644 --- a/lib/data/dao/group_dao.dart +++ b/lib/data/dao/group_dao.dart @@ -143,16 +143,16 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { final query = select(groupTable); final result = await query.get(); return Future.wait( - result.map((groupData) async { + result.map((row) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: groupData.id, + groupId: row.id, ); return Group( - id: groupData.id, - name: groupData.name, - description: groupData.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: groupData.createdAt, + createdAt: row.createdAt, ); }), ); @@ -161,18 +161,18 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Retrieves a [Group] by its [groupId], including its members. Future getGroupById({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingle(); + final row = await query.getSingle(); List members = await db.playerGroupDao.getPlayersOfGroup( groupId: groupId, ); return Group( - id: result.id, - name: result.name, - description: result.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: result.createdAt, + createdAt: row.createdAt, ); } @@ -180,7 +180,7 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future getGroupCount() async { final count = await (selectOnly(groupTable)..addColumns([groupTable.id.count()])) - .map((row) => row.read(groupTable.id.count())) + .map((tbl) => tbl.read(groupTable.id.count())) .getSingle(); return count ?? 0; } @@ -190,28 +190,28 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { Future> getGroupsByPlayer({required String playerId}) async { final playerGroups = await (select( playerGroupTable, - )..where((pg) => pg.playerId.equals(playerId))).get(); + )..where((tbl) => tbl.playerId.equals(playerId))).get(); if (playerGroups.isEmpty) return []; final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList(); - final rows = + final result = await (select(groupTable) - ..where((g) => g.id.isIn(groupIds)) - ..orderBy([(g) => OrderingTerm.desc(g.createdAt)])) + ..where((tbl) => tbl.id.isIn(groupIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) .get(); return Future.wait( - rows.map((groupData) async { + result.map((row) async { final members = await db.playerGroupDao.getPlayersOfGroup( - groupId: groupData.id, + groupId: row.id, ); return Group( - id: groupData.id, - name: groupData.name, - description: groupData.description, + id: row.id, + name: row.name, + description: row.description, members: members, - createdAt: groupData.createdAt, + createdAt: row.createdAt, ); }), ); @@ -221,8 +221,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { /// Returns `true` if the group exists, `false` otherwise. Future groupExists({required String groupId}) async { final query = select(groupTable)..where((g) => g.id.equals(groupId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /* Delete */ @@ -252,9 +252,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String name, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(name: Value(name)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(name: Value(name))); return rowsAffected > 0; } @@ -265,9 +264,8 @@ class GroupDao extends DatabaseAccessor with _$GroupDaoMixin { required String description, }) async { final rowsAffected = - await (update(groupTable)..where((g) => g.id.equals(groupId))).write( - GroupTableCompanion(description: Value(description)), - ); + await (update(groupTable)..where((tbl) => tbl.id.equals(groupId))) + .write(GroupTableCompanion(description: Value(description))); return rowsAffected > 0; } } diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index e8414f4..74611b6 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -258,15 +258,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Returns `true` if the match exists, otherwise `false`. Future matchExists({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// 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())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -279,10 +279,12 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return Future.wait( result.map((row) async { 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, ); @@ -312,13 +314,13 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Retrieves a [Match] by its [matchId]. Future getMatchById({required String matchId}) async { final query = select(matchTable)..where((g) => g.id.equals(matchId)); - final result = await query.getSingle(); + final row = await query.getSingle(); - final game = await db.gameDao.getGameById(gameId: result.gameId); + final game = await db.gameDao.getGameById(gameId: row.gameId); Group? group; - if (result.groupId != null) { - group = await db.groupDao.getGroupById(groupId: result.groupId!); + if (row.groupId != null) { + group = await db.groupDao.getGroupById(groupId: row.groupId!); } final players = await db.playerMatchDao.getPlayersOfMatch(matchId: matchId); @@ -328,15 +330,15 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { final teams = await _getMatchTeams(matchId: matchId); return Match( - id: result.id, - name: result.name, + id: row.id, + name: row.name, game: game, group: group, players: players, teams: teams.isEmpty ? null : teams, - notes: result.notes, - createdAt: result.createdAt, - endedAt: result.endedAt, + notes: row.notes, + createdAt: row.createdAt, + endedAt: row.endedAt, scores: scores, ); } @@ -347,7 +349,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { await (selectOnly(matchTable) ..where(matchTable.gameId.equals(gameId)) ..addColumns([matchTable.id.count()])) - .map((row) => row.read(matchTable.id.count())) + .map((tbl) => tbl.read(matchTable.id.count())) .getSingle(); return count ?? 0; } @@ -355,19 +357,19 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> getMatchesByPlayer({required String playerId}) async { final playerMatches = await (select( playerMatchTable, - )..where((pm) => pm.playerId.equals(playerId))).get(); + )..where((tbl) => tbl.playerId.equals(playerId))).get(); if (playerMatches.isEmpty) return []; - final matchIds = playerMatches.map((pm) => pm.matchId).toSet().toList(); - final rows = + final matchIds = playerMatches.map((tbl) => tbl.matchId).toSet().toList(); + final result = await (select(matchTable) - ..where((m) => m.id.isIn(matchIds)) - ..orderBy([(m) => OrderingTerm.desc(m.createdAt)])) + ..where((tbl) => tbl.id.isIn(matchIds)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.createdAt)])) .get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); Group? group; @@ -403,16 +405,17 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Queries the database directly, filtering by [groupId]. Future> getMatchesByGroup({required String groupId}) async { final query = select(matchTable)..where((m) => m.groupId.equals(groupId)); - final rows = await query.get(); + final result = await query.get(); return Future.wait( - rows.map((row) async { + result.map((row) async { final game = await db.gameDao.getGameById(gameId: row.gameId); final group = await db.groupDao.getGroupById(groupId: groupId); final players = await db.playerMatchDao.getPlayersOfMatch( matchId: row.id, ); final teams = await _getMatchTeams(matchId: row.id); + return Match( id: row.id, name: row.name, @@ -432,7 +435,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { Future> _getMatchTeams({required String matchId}) async { // Get all unique team IDs from PlayerMatchTable for this match final playerMatchQuery = select(db.playerMatchTable) - ..where((pm) => pm.matchId.equals(matchId) & pm.teamId.isNotNull()); + ..where((tbl) => tbl.matchId.equals(matchId) & tbl.teamId.isNotNull()); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; @@ -459,7 +462,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String name, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(name: Value(name)), ); @@ -474,7 +477,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String? groupId, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(groupId: Value(groupId)), ); @@ -487,7 +490,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required String notes, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(notes: Value(notes)), ); @@ -498,7 +501,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Sets the groupId to null. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future removeMatchGroup({required String matchId}) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( const MatchTableCompanion(groupId: Value(null)), ); @@ -512,7 +515,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { required String matchId, required DateTime endedAt, }) async { - final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final query = update(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.write( MatchTableCompanion(endedAt: Value(endedAt)), ); @@ -524,7 +527,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes the match with the given [matchId] from the database. /// Returns `true` if more than 0 rows were affected, otherwise `false`. Future deleteMatch({required String matchId}) async { - final query = delete(matchTable)..where((g) => g.id.equals(matchId)); + final query = delete(matchTable)..where((tbl) => tbl.id.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -540,7 +543,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { /// Deletes all matches associated with a specific game. /// Returns the number of matches deleted. Future deleteMatchesByGame({required String gameId}) async { - final query = delete(matchTable)..where((m) => m.gameId.equals(gameId)); + final query = delete(matchTable)..where((tbl) => tbl.gameId.equals(gameId)); final rowsAffected = await query.go(); return rowsAffected; } diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index a6fd1c5..1a60243 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -113,7 +113,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { Future getPlayerCount() async { final count = await (selectOnly(playerTable)..addColumns([playerTable.id.count()])) - .map((row) => row.read(playerTable.id.count())) + .map((tbl) => tbl.read(playerTable.id.count())) .getSingle(); return count ?? 0; } @@ -122,8 +122,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns `true` if the player exists, `false` otherwise. Future playerExists({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingleOrNull(); - return result != null; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all players from the database. @@ -146,13 +146,13 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Retrieves a [Player] by their [id]. Future getPlayerById({required String playerId}) async { final query = select(playerTable)..where((p) => p.id.equals(playerId)); - final result = await query.getSingle(); + final row = await query.getSingle(); return Player( - id: result.id, - name: result.name, - description: result.description, - createdAt: result.createdAt, - nameCount: result.nameCount, + id: row.id, + name: row.name, + description: row.description, + createdAt: row.createdAt, + nameCount: row.nameCount, ); } @@ -174,7 +174,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { return transaction(() async { final previousPlayer = await (select( playerTable, - )..where((p) => p.id.equals(playerId))).getSingleOrNull(); + )..where((tbl) => tbl.id.equals(playerId))).getSingleOrNull(); if (previousPlayer == null) return false; final previousName = previousPlayer.name; @@ -186,7 +186,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final rowsAffected = await (update( playerTable, - )..where((p) => p.id.equals(playerId))).write( + )..where((tbl) => tbl.id.equals(playerId))).write( PlayerTableCompanion( name: Value(name), nameCount: Value(newNameCount), @@ -203,9 +203,9 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { } else if (remainingCount > 1 && previousCount > 0) { // Shift every player above the gap down by one to keep numbering in order. await (update(playerTable)..where( - (p) => - p.name.equals(previousName) & - p.nameCount.isBiggerThanValue(previousCount), + (tbl) => + tbl.name.equals(previousName) & + tbl.nameCount.isBiggerThanValue(previousCount), )) .write( PlayerTableCompanion.custom( @@ -226,9 +226,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String description, }) async { final rowsAffected = - await (update(playerTable)..where((g) => g.id.equals(playerId))).write( - PlayerTableCompanion(description: Value(description)), - ); + await (update(playerTable)..where((tbl) => tbl.id.equals(playerId))) + .write(PlayerTableCompanion(description: Value(description))); return rowsAffected > 0; } @@ -237,7 +236,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Deletes the player with the given [id] from the database. /// Returns `true` if the player was deleted, `false` if the player did not exist. Future deletePlayer({required String playerId}) async { - final query = delete(playerTable)..where((p) => p.id.equals(playerId)); + final query = delete(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -248,7 +247,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /// Returns the highest name count if players with the same name exist, /// otherwise `null`. Future getNameCount({required String name}) async { - final query = select(playerTable)..where((p) => p.name.equals(name)); + final query = select(playerTable)..where((tbl) => tbl.name.equals(name)); final result = await query.get(); return result.length; } @@ -259,7 +258,7 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { required String playerId, required int nameCount, }) async { - final query = update(playerTable)..where((p) => p.id.equals(playerId)); + final query = update(playerTable)..where((tbl) => tbl.id.equals(playerId)); final rowsAffected = await query.write( PlayerTableCompanion(nameCount: Value(nameCount)), ); @@ -269,8 +268,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future getPlayerWithHighestNameCount({required String name}) async { final query = select(playerTable) - ..where((p) => p.name.equals(name)) - ..orderBy([(p) => OrderingTerm.desc(p.nameCount)]) + ..where((tbl) => tbl.name.equals(name)) + ..orderBy([(tbl) => OrderingTerm.desc(tbl.nameCount)]) ..limit(1); final result = await query.getSingleOrNull(); if (result != null) { @@ -324,9 +323,8 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { @visibleForTesting Future initializeNameCount({required String name}) async { final rowsAffected = - await (update(playerTable)..where((p) => p.name.equals(name))).write( - const PlayerTableCompanion(nameCount: Value(1)), - ); + await (update(playerTable)..where((tbl) => tbl.name.equals(name))) + .write(const PlayerTableCompanion(nameCount: Value(1))); return rowsAffected > 0; } diff --git a/lib/data/dao/player_group_dao.dart b/lib/data/dao/player_group_dao.dart index 48c5653..b48dc23 100644 --- a/lib/data/dao/player_group_dao.dart +++ b/lib/data/dao/player_group_dao.dart @@ -39,18 +39,25 @@ class PlayerGroupDao extends DatabaseAccessor /// Retrieves all players belonging to a specific group by [groupId]. Future> getPlayersOfGroup({required String groupId}) async { - final query = select(playerGroupTable) - ..where((pG) => pG.groupId.equals(groupId)); - final result = await query.get(); + final query = select(playerGroupTable).join([ + innerJoin( + playerTable, + playerTable.id.equalsExp(playerGroupTable.playerId), + ), + ])..where(playerGroupTable.groupId.equals(groupId)); - List groupMembers = List.empty(growable: true); - - for (var entry in result) { - final player = await db.playerDao.getPlayerById(playerId: entry.playerId); - groupMembers.add(player); - } - - return groupMembers; + final result = await query.map((row) => row.readTable(playerTable)).get(); + return result + .map( + (row) => Player( + id: row.id, + createdAt: row.createdAt, + name: row.name, + nameCount: row.nameCount, + description: row.description, + ), + ) + .toList(); } /// Checks if a player with [playerId] is in the group with [groupId]. @@ -60,7 +67,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = select(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final result = await query.getSingleOrNull(); return result != null; } @@ -81,7 +90,7 @@ class PlayerGroupDao extends DatabaseAccessor await db.transaction(() async { // Remove all existing players from the group final deleteQuery = delete(db.playerGroupTable) - ..where((p) => p.groupId.equals(groupId)); + ..where((tbl) => tbl.groupId.equals(groupId)); await deleteQuery.go(); // Add new players to the player table if they don't exist @@ -121,7 +130,9 @@ class PlayerGroupDao extends DatabaseAccessor required String groupId, }) async { final query = delete(playerGroupTable) - ..where((p) => p.playerId.equals(playerId) & p.groupId.equals(groupId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.groupId.equals(groupId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/player_match_dao.dart b/lib/data/dao/player_match_dao.dart index d119468..912cfcc 100644 --- a/lib/data/dao/player_match_dao.dart +++ b/lib/data/dao/player_match_dao.dart @@ -40,7 +40,7 @@ class PlayerMatchDao extends DatabaseAccessor await (selectOnly(playerMatchTable) ..where(playerMatchTable.matchId.equals(matchId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -56,7 +56,7 @@ class PlayerMatchDao extends DatabaseAccessor ..where(playerMatchTable.matchId.equals(matchId)) ..where(playerMatchTable.playerId.equals(playerId)) ..addColumns([playerMatchTable.playerId.count()])) - .map((row) => row.read(playerMatchTable.playerId.count())) + .map((tbl) => tbl.read(playerMatchTable.playerId.count())) .getSingle(); return (count ?? 0) > 0; } @@ -66,7 +66,7 @@ class PlayerMatchDao extends DatabaseAccessor Future> getPlayersOfMatch({required String matchId}) async { final result = await (select( playerMatchTable, - )..where((p) => p.matchId.equals(matchId))).get(); + )..where((tbl) => tbl.matchId.equals(matchId))).get(); if (result.isEmpty) return []; @@ -85,8 +85,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final result = await (select(playerMatchTable) - ..where((p) => p.matchId.equals(matchId)) - ..where((p) => p.teamId.equals(teamId))) + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.teamId.equals(teamId))) .get(); if (result.isEmpty) return []; @@ -109,7 +109,8 @@ class PlayerMatchDao extends DatabaseAccessor }) async { final rowsAffected = await (update(playerMatchTable)..where( - (p) => p.matchId.equals(matchId) & p.playerId.equals(playerId), + (tbl) => + tbl.matchId.equals(matchId) & tbl.playerId.equals(playerId), )) .write(PlayerMatchTableCompanion(teamId: Value(teamId))); return rowsAffected > 0; @@ -143,9 +144,9 @@ class PlayerMatchDao extends DatabaseAccessor // Remove old players if (playersToRemove.isNotEmpty) { await (delete(playerMatchTable)..where( - (pg) => - pg.matchId.equals(matchId) & - pg.playerId.isIn(playersToRemove.toList()), + (tbl) => + tbl.matchId.equals(matchId) & + tbl.playerId.isIn(playersToRemove.toList()), )) .go(); } @@ -182,8 +183,8 @@ class PlayerMatchDao extends DatabaseAccessor required String playerId, }) async { final query = delete(playerMatchTable) - ..where((pg) => pg.matchId.equals(matchId)) - ..where((pg) => pg.playerId.equals(playerId)); + ..where((tbl) => tbl.matchId.equals(matchId)) + ..where((tbl) => tbl.playerId.equals(playerId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/score_entry_dao.dart b/lib/data/dao/score_entry_dao.dart index 830135d..276e8fd 100644 --- a/lib/data/dao/score_entry_dao.dart +++ b/lib/data/dao/score_entry_dao.dart @@ -70,10 +70,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = select(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final result = await query.getSingleOrNull(); @@ -91,7 +91,7 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final result = await query.get(); final Map scoresByPlayer = {}; @@ -113,8 +113,10 @@ class ScoreEntryDao extends DatabaseAccessor required String matchId, }) async { final query = select(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)) - ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ) + ..orderBy([(tbl) => OrderingTerm.asc(tbl.roundNumber)]); final result = await query.get(); return result .map( @@ -136,8 +138,8 @@ class ScoreEntryDao extends DatabaseAccessor final query = selectOnly(scoreEntryTable) ..where(scoreEntryTable.matchId.equals(matchId)) ..addColumns([scoreEntryTable.roundNumber.max()]); - final result = await query.getSingle(); - return result.read(scoreEntryTable.roundNumber.max()); + final row = await query.getSingle(); + return row.read(scoreEntryTable.roundNumber.max()); } /// Aggregates the total score for a player in a match by summing all their @@ -166,10 +168,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final rowsAffected = await (update(scoreEntryTable)..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(entry.roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(entry.roundNumber), )) .write( ScoreEntryTableCompanion( @@ -190,10 +192,10 @@ class ScoreEntryDao extends DatabaseAccessor }) async { final query = delete(scoreEntryTable) ..where( - (s) => - s.playerId.equals(playerId) & - s.matchId.equals(matchId) & - s.roundNumber.equals(roundNumber), + (tbl) => + tbl.playerId.equals(playerId) & + tbl.matchId.equals(matchId) & + tbl.roundNumber.equals(roundNumber), ); final rowsAffected = await query.go(); return rowsAffected > 0; @@ -201,7 +203,7 @@ class ScoreEntryDao extends DatabaseAccessor Future deleteAllScoresForMatch({required String matchId}) async { final query = delete(scoreEntryTable) - ..where((s) => s.matchId.equals(matchId)); + ..where((tbl) => tbl.matchId.equals(matchId)); final rowsAffected = await query.go(); return rowsAffected > 0; } @@ -211,7 +213,9 @@ class ScoreEntryDao extends DatabaseAccessor required String playerId, }) async { final query = delete(scoreEntryTable) - ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)); + ..where( + (tbl) => tbl.playerId.equals(playerId) & tbl.matchId.equals(matchId), + ); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/dao/statistic_dao.dart b/lib/data/dao/statistic_dao.dart new file mode 100644 index 0000000..092ceb0 --- /dev/null +++ b/lib/data/dao/statistic_dao.dart @@ -0,0 +1,127 @@ +import 'package:collection/collection.dart'; +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; +import 'package:tallee/data/models/statistic.dart'; + +part 'statistic_dao.g.dart'; + +@DriftAccessor(tables: [StatisticTable]) +class StatisticDao extends DatabaseAccessor + with _$StatisticDaoMixin { + StatisticDao(super.db); + + /* Create */ + + Future addStatistic({required Statistic statistic}) async { + await into(statisticTable).insert( + StatisticTableCompanion.insert( + id: statistic.id, + type: statistic.type.name, + timeframe: Value(statistic.timeframe?.name), + displayCount: Value(statistic.displayCount), + ), + mode: InsertMode.insertOrReplace, + ); + + await db.statisticScopeDao.addStatisticScopes( + statisticId: statistic.id, + scopes: statistic.scopes, + ); + + if (statistic.selectedGroups != null) { + await db.statisticGroupDao.addStatisticGroups( + statisticId: statistic.id, + groups: statistic.selectedGroups!, + ); + } + + if (statistic.selectedGames != null) { + await db.statisticGameDao.addStatisticGames( + statisticId: statistic.id, + games: statistic.selectedGames!, + ); + } + + return true; + } + + /* Read */ + + Future getStatisticById(String statisticId) async { + final query = select(statisticTable); + final row = await query.getSingleOrNull(); + if (row != null) { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere((type) => type.name == row.type), + scopes: scopes, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + displayCount: row.displayCount, + id: row.id, + ); + } + return null; + } + + /// Retrieves all statistics from the database, including their associated groups and games. + Future> getAllStatistics() async { + final query = select(statisticTable); + final result = await query.get(); + return Future.wait( + result.map((row) async { + final groups = await db.statisticGroupDao.getGroupsForStatistic(row.id); + final games = await db.statisticGameDao.getGamesForStatistic(row.id); + final scopes = await db.statisticScopeDao.getScopeForStatistic(row.id); + + return Statistic( + type: StatisticType.values.firstWhere( + (type) => type.name == row.type, + ), + scopes: scopes, + timeframe: Timeframe.values.firstWhereOrNull( + (t) => t.name == row.timeframe, + ), + selectedGroups: groups, + selectedGames: games, + displayCount: row.displayCount, + id: row.id, + ); + }), + ); + } + + /* Update */ + + Future updateDisplayCount(String statisticId, int displayCount) async { + final rowsUpdated = + await (update(statisticTable) + ..where((tbl) => tbl.id.equals(statisticId))) + .write(StatisticTableCompanion(displayCount: Value(displayCount))); + + return rowsUpdated > 0; + } + + /* Delete */ + + Future deleteStatistic(String statisticId) async { + final rowsDeleted = await (delete( + statisticTable, + )..where((tbl) => tbl.id.equals(statisticId))).go(); + + return rowsDeleted > 0; + } + + Future deleteAllStatistics() async { + final rowsDeleted = await delete(statisticTable).go(); + return rowsDeleted > 0; + } +} diff --git a/lib/data/dao/statistic_dao.g.dart b/lib/data/dao/statistic_dao.g.dart new file mode 100644 index 0000000..0ce36e1 --- /dev/null +++ b/lib/data/dao/statistic_dao.g.dart @@ -0,0 +1,19 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + StatisticDaoManager get managers => StatisticDaoManager(this); +} + +class StatisticDaoManager { + final _$StatisticDaoMixin _db; + StatisticDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); +} diff --git a/lib/data/dao/statistic_game_dao.dart b/lib/data/dao/statistic_game_dao.dart new file mode 100644 index 0000000..d546ce0 --- /dev/null +++ b/lib/data/dao/statistic_game_dao.dart @@ -0,0 +1,61 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/models/game.dart'; + +part 'statistic_game_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGameTable]) +class StatisticGameDao extends DatabaseAccessor + with _$StatisticGameDaoMixin { + StatisticGameDao(super.db); + + /// Retrieves a list of games associated with a specific statistic. + Future?> getGamesForStatistic(String statisticId) async { + final query = select(statisticGameTable).join([ + innerJoin(gameTable, gameTable.id.equalsExp(statisticGameTable.gameId)), + ])..where(statisticGameTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(gameTable)).get(); + if (results.isEmpty) return null; + return results + .map( + (row) => Game( + id: row.id, + name: row.name, + ruleset: Ruleset.values.firstWhere((e) => e.name == row.ruleset), + description: row.description, + color: AppColor.values.firstWhere((e) => e.name == row.color), + icon: row.icon, + createdAt: row.createdAt, + ), + ) + .toList(); + } + + Future addStatisticGames({ + required String statisticId, + required List games, + }) { + final entries = games + .map( + (game) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: game.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGameTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic games: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_game_dao.g.dart b/lib/data/dao/statistic_game_dao.g.dart new file mode 100644 index 0000000..d6ee984 --- /dev/null +++ b/lib/data/dao/statistic_game_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_game_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGameDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GameTableTable get gameTable => attachedDatabase.gameTable; + $StatisticGameTableTable get statisticGameTable => + attachedDatabase.statisticGameTable; + StatisticGameDaoManager get managers => StatisticGameDaoManager(this); +} + +class StatisticGameDaoManager { + final _$StatisticGameDaoMixin _db; + StatisticGameDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GameTableTableTableManager get gameTable => + $$GameTableTableTableManager(_db.attachedDatabase, _db.gameTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager( + _db.attachedDatabase, + _db.statisticGameTable, + ); +} diff --git a/lib/data/dao/statistic_group_dao.dart b/lib/data/dao/statistic_group_dao.dart new file mode 100644 index 0000000..449b6a8 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.dart @@ -0,0 +1,67 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/models/group.dart'; + +part 'statistic_group_dao.g.dart'; + +@DriftAccessor(tables: [StatisticGroupTable, GroupTable]) +class StatisticGroupDao extends DatabaseAccessor + with _$StatisticGroupDaoMixin { + StatisticGroupDao(super.db); + + /// Retrieves a list of groups associated with a specific statistic. + Future?> getGroupsForStatistic(String statisticId) async { + final query = select(statisticGroupTable).join([ + innerJoin( + groupTable, + groupTable.id.equalsExp(statisticGroupTable.groupId), + ), + ])..where(statisticGroupTable.statisticId.equals(statisticId)); + + final results = await query.map((row) => row.readTable(groupTable)).get(); + if (results.isEmpty) return null; + final groups = await Future.wait( + results.map((result) async { + final groupMembers = await db.playerGroupDao.getPlayersOfGroup( + groupId: result.id, + ); + return Group( + id: result.id, + createdAt: result.createdAt, + name: result.name, + description: result.description, + members: groupMembers, + ); + }), + ); + + return groups; + } + + Future addStatisticGroups({ + required String statisticId, + required List groups, + }) async { + final entries = groups + .map( + (group) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: group.id, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticGroupTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic groups: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_group_dao.g.dart b/lib/data/dao/statistic_group_dao.g.dart new file mode 100644 index 0000000..57a83c5 --- /dev/null +++ b/lib/data/dao/statistic_group_dao.g.dart @@ -0,0 +1,29 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_group_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticGroupDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $GroupTableTable get groupTable => attachedDatabase.groupTable; + $StatisticGroupTableTable get statisticGroupTable => + attachedDatabase.statisticGroupTable; + StatisticGroupDaoManager get managers => StatisticGroupDaoManager(this); +} + +class StatisticGroupDaoManager { + final _$StatisticGroupDaoMixin _db; + StatisticGroupDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$GroupTableTableTableManager get groupTable => + $$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager( + _db.attachedDatabase, + _db.statisticGroupTable, + ); +} diff --git a/lib/data/dao/statistic_scope_dao.dart b/lib/data/dao/statistic_scope_dao.dart new file mode 100644 index 0000000..eb286af --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.dart @@ -0,0 +1,55 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; + +part 'statistic_scope_dao.g.dart'; + +@DriftAccessor(tables: [StatisticScopeTable]) +class StatisticScopeDao extends DatabaseAccessor + with _$StatisticScopeDaoMixin { + StatisticScopeDao(super.db); + + /// Retrieves a list of statistic scopes associated with a specific statistic ID. + Future> getScopeForStatistic(String statisticId) async { + final query = select(statisticScopeTable) + ..where((tbl) => tbl.statisticId.equals(statisticId)); + + final result = await query.get(); + return result + .map( + (row) => StatisticScope.values.firstWhere( + (e) => e.name == row.scope, + orElse: () => throw Exception( + 'Invalid scope value: ${row.scope} for statistic ID: $statisticId', + ), + ), + ) + .toList(); + } + + Future addStatisticScopes({ + required String statisticId, + required List scopes, + }) async { + final entries = scopes + .map( + (scope) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope.name, + ), + ) + .toList(); + + return batch((batch) { + batch.insertAll( + statisticScopeTable, + entries, + mode: InsertMode.insertOrReplace, + ); + }).then((_) => true).catchError((error) { + print('Error adding statistic scopes: $error'); + return false; + }); + } +} diff --git a/lib/data/dao/statistic_scope_dao.g.dart b/lib/data/dao/statistic_scope_dao.g.dart new file mode 100644 index 0000000..adaa171 --- /dev/null +++ b/lib/data/dao/statistic_scope_dao.g.dart @@ -0,0 +1,26 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'statistic_scope_dao.dart'; + +// ignore_for_file: type=lint +mixin _$StatisticScopeDaoMixin on DatabaseAccessor { + $StatisticTableTable get statisticTable => attachedDatabase.statisticTable; + $StatisticScopeTableTable get statisticScopeTable => + attachedDatabase.statisticScopeTable; + StatisticScopeDaoManager get managers => StatisticScopeDaoManager(this); +} + +class StatisticScopeDaoManager { + final _$StatisticScopeDaoMixin _db; + StatisticScopeDaoManager(this._db); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager( + _db.attachedDatabase, + _db.statisticTable, + ); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager( + _db.attachedDatabase, + _db.statisticScopeTable, + ); +} diff --git a/lib/data/dao/team_dao.dart b/lib/data/dao/team_dao.dart index cba68fb..333db68 100644 --- a/lib/data/dao/team_dao.dart +++ b/lib/data/dao/team_dao.dart @@ -86,7 +86,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { Future getTeamCount() async { final count = await (selectOnly(teamTable)..addColumns([teamTable.id.count()])) - .map((row) => row.read(teamTable.id.count())) + .map((tbl) => tbl.read(teamTable.id.count())) .getSingle(); return count ?? 0; } @@ -95,8 +95,8 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// 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; + final row = await query.getSingleOrNull(); + return row != null; } /// Retrieves all teams from the database. @@ -119,12 +119,12 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// 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 row = await query.getSingle(); final members = await _getTeamMembers(teamId: teamId); return Team( - id: result.id, - name: result.name, - createdAt: result.createdAt, + id: row.id, + name: row.name, + createdAt: row.createdAt, members: members, ); } @@ -133,13 +133,13 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { 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)); + ..where((tbl) => tbl.teamId.equals(teamId)); final playerMatches = await playerMatchQuery.get(); if (playerMatches.isEmpty) return []; // Get unique player IDs - final playerIds = playerMatches.map((pm) => pm.playerId).toSet(); + final playerIds = playerMatches.map((tbl) => tbl.playerId).toSet(); // Fetch all players final players = await Future.wait( @@ -156,7 +156,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { required String name, }) async { final rowsAffected = - await (update(teamTable)..where((t) => t.id.equals(teamId))).write( + await (update(teamTable)..where((tbl) => tbl.id.equals(teamId))).write( TeamTableCompanion(name: Value(name)), ); return rowsAffected > 0; @@ -175,7 +175,7 @@ class TeamDao extends DatabaseAccessor with _$TeamDaoMixin { /// 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 query = delete(teamTable)..where((tbl) => tbl.id.equals(teamId)); final rowsAffected = await query.go(); return rowsAffected > 0; } diff --git a/lib/data/db/database.dart b/lib/data/db/database.dart index a7e9c1d..792da0e 100644 --- a/lib/data/db/database.dart +++ b/lib/data/db/database.dart @@ -8,6 +8,10 @@ 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/dao/score_entry_dao.dart'; +import 'package:tallee/data/dao/statistic_dao.dart'; +import 'package:tallee/data/dao/statistic_game_dao.dart'; +import 'package:tallee/data/dao/statistic_group_dao.dart'; +import 'package:tallee/data/dao/statistic_scope_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'; @@ -16,6 +20,10 @@ 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_entry_table.dart'; +import 'package:tallee/data/db/tables/statistic_game_table.dart'; +import 'package:tallee/data/db/tables/statistic_group_table.dart'; +import 'package:tallee/data/db/tables/statistic_scope_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; import 'package:tallee/data/db/tables/team_table.dart'; part 'database.g.dart'; @@ -30,6 +38,10 @@ part 'database.g.dart'; GameTable, TeamTable, ScoreEntryTable, + StatisticTable, + StatisticScopeTable, + StatisticGameTable, + StatisticGroupTable, ], daos: [ PlayerDao, @@ -40,6 +52,10 @@ part 'database.g.dart'; GameDao, ScoreEntryDao, TeamDao, + StatisticDao, + StatisticScopeDao, + StatisticGameDao, + StatisticGroupDao, ], ) class AppDatabase extends _$AppDatabase { diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index c8d0faa..3a9d277 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -2732,6 +2732,1020 @@ class ScoreEntryTableCompanion extends UpdateCompanion { } } +class $StatisticTableTable extends StatisticTable + with TableInfo<$StatisticTableTable, StatisticTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticTableTable(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 _typeMeta = const VerificationMeta('type'); + @override + late final GeneratedColumn type = GeneratedColumn( + 'type', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + static const VerificationMeta _timeframeMeta = const VerificationMeta( + 'timeframe', + ); + @override + late final GeneratedColumn timeframe = GeneratedColumn( + 'timeframe', + aliasedName, + true, + type: DriftSqlType.string, + requiredDuringInsert: false, + ); + static const VerificationMeta _displayCountMeta = const VerificationMeta( + 'displayCount', + ); + @override + late final GeneratedColumn displayCount = GeneratedColumn( + 'display_count', + aliasedName, + false, + type: DriftSqlType.int, + requiredDuringInsert: false, + defaultValue: const Constant(5), + ); + @override + List get $columns => [id, type, timeframe, displayCount]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_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('type')) { + context.handle( + _typeMeta, + type.isAcceptableOrUnknown(data['type']!, _typeMeta), + ); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('timeframe')) { + context.handle( + _timeframeMeta, + timeframe.isAcceptableOrUnknown(data['timeframe']!, _timeframeMeta), + ); + } + if (data.containsKey('display_count')) { + context.handle( + _displayCountMeta, + displayCount.isAcceptableOrUnknown( + data['display_count']!, + _displayCountMeta, + ), + ); + } + return context; + } + + @override + Set get $primaryKey => {id}; + @override + StatisticTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticTableData( + id: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}id'], + )!, + type: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}type'], + )!, + timeframe: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}timeframe'], + ), + displayCount: attachedDatabase.typeMapping.read( + DriftSqlType.int, + data['${effectivePrefix}display_count'], + )!, + ); + } + + @override + $StatisticTableTable createAlias(String alias) { + return $StatisticTableTable(attachedDatabase, alias); + } +} + +class StatisticTableData extends DataClass + implements Insertable { + final String id; + final String type; + final String? timeframe; + final int displayCount; + const StatisticTableData({ + required this.id, + required this.type, + this.timeframe, + required this.displayCount, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['id'] = Variable(id); + map['type'] = Variable(type); + if (!nullToAbsent || timeframe != null) { + map['timeframe'] = Variable(timeframe); + } + map['display_count'] = Variable(displayCount); + return map; + } + + StatisticTableCompanion toCompanion(bool nullToAbsent) { + return StatisticTableCompanion( + id: Value(id), + type: Value(type), + timeframe: timeframe == null && nullToAbsent + ? const Value.absent() + : Value(timeframe), + displayCount: Value(displayCount), + ); + } + + factory StatisticTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticTableData( + id: serializer.fromJson(json['id']), + type: serializer.fromJson(json['type']), + timeframe: serializer.fromJson(json['timeframe']), + displayCount: serializer.fromJson(json['displayCount']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'id': serializer.toJson(id), + 'type': serializer.toJson(type), + 'timeframe': serializer.toJson(timeframe), + 'displayCount': serializer.toJson(displayCount), + }; + } + + StatisticTableData copyWith({ + String? id, + String? type, + Value timeframe = const Value.absent(), + int? displayCount, + }) => StatisticTableData( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe.present ? timeframe.value : this.timeframe, + displayCount: displayCount ?? this.displayCount, + ); + StatisticTableData copyWithCompanion(StatisticTableCompanion data) { + return StatisticTableData( + id: data.id.present ? data.id.value : this.id, + type: data.type.present ? data.type.value : this.type, + timeframe: data.timeframe.present ? data.timeframe.value : this.timeframe, + displayCount: data.displayCount.present + ? data.displayCount.value + : this.displayCount, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticTableData(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(id, type, timeframe, displayCount); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticTableData && + other.id == this.id && + other.type == this.type && + other.timeframe == this.timeframe && + other.displayCount == this.displayCount); +} + +class StatisticTableCompanion extends UpdateCompanion { + final Value id; + final Value type; + final Value timeframe; + final Value displayCount; + final Value rowid; + const StatisticTableCompanion({ + this.id = const Value.absent(), + this.type = const Value.absent(), + this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticTableCompanion.insert({ + required String id, + required String type, + this.timeframe = const Value.absent(), + this.displayCount = const Value.absent(), + this.rowid = const Value.absent(), + }) : id = Value(id), + type = Value(type); + static Insertable custom({ + Expression? id, + Expression? type, + Expression? timeframe, + Expression? displayCount, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (id != null) 'id': id, + if (type != null) 'type': type, + if (timeframe != null) 'timeframe': timeframe, + if (displayCount != null) 'display_count': displayCount, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticTableCompanion copyWith({ + Value? id, + Value? type, + Value? timeframe, + Value? displayCount, + Value? rowid, + }) { + return StatisticTableCompanion( + id: id ?? this.id, + type: type ?? this.type, + timeframe: timeframe ?? this.timeframe, + displayCount: displayCount ?? this.displayCount, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (id.present) { + map['id'] = Variable(id.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (timeframe.present) { + map['timeframe'] = Variable(timeframe.value); + } + if (displayCount.present) { + map['display_count'] = Variable(displayCount.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticTableCompanion(') + ..write('id: $id, ') + ..write('type: $type, ') + ..write('timeframe: $timeframe, ') + ..write('displayCount: $displayCount, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticScopeTableTable extends StatisticScopeTable + with TableInfo<$StatisticScopeTableTable, StatisticScopeTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticScopeTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _scopeMeta = const VerificationMeta('scope'); + @override + late final GeneratedColumn scope = GeneratedColumn( + 'scope', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + ); + @override + List get $columns => [statisticId, scope]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_scope_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('scope')) { + context.handle( + _scopeMeta, + scope.isAcceptableOrUnknown(data['scope']!, _scopeMeta), + ); + } else if (isInserting) { + context.missing(_scopeMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, scope}; + @override + StatisticScopeTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticScopeTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + scope: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}scope'], + )!, + ); + } + + @override + $StatisticScopeTableTable createAlias(String alias) { + return $StatisticScopeTableTable(attachedDatabase, alias); + } +} + +class StatisticScopeTableData extends DataClass + implements Insertable { + final String statisticId; + final String scope; + const StatisticScopeTableData({ + required this.statisticId, + required this.scope, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['scope'] = Variable(scope); + return map; + } + + StatisticScopeTableCompanion toCompanion(bool nullToAbsent) { + return StatisticScopeTableCompanion( + statisticId: Value(statisticId), + scope: Value(scope), + ); + } + + factory StatisticScopeTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticScopeTableData( + statisticId: serializer.fromJson(json['statisticId']), + scope: serializer.fromJson(json['scope']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'scope': serializer.toJson(scope), + }; + } + + StatisticScopeTableData copyWith({String? statisticId, String? scope}) => + StatisticScopeTableData( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + ); + StatisticScopeTableData copyWithCompanion(StatisticScopeTableCompanion data) { + return StatisticScopeTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + scope: data.scope.present ? data.scope.value : this.scope, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableData(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, scope); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticScopeTableData && + other.statisticId == this.statisticId && + other.scope == this.scope); +} + +class StatisticScopeTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value scope; + final Value rowid; + const StatisticScopeTableCompanion({ + this.statisticId = const Value.absent(), + this.scope = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticScopeTableCompanion.insert({ + required String statisticId, + required String scope, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + scope = Value(scope); + static Insertable custom({ + Expression? statisticId, + Expression? scope, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (scope != null) 'scope': scope, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticScopeTableCompanion copyWith({ + Value? statisticId, + Value? scope, + Value? rowid, + }) { + return StatisticScopeTableCompanion( + statisticId: statisticId ?? this.statisticId, + scope: scope ?? this.scope, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (scope.present) { + map['scope'] = Variable(scope.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticScopeTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('scope: $scope, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGameTableTable extends StatisticGameTable + with TableInfo<$StatisticGameTableTable, StatisticGameTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGameTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + 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', + ), + ); + @override + List get $columns => [statisticId, gameId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_game_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('game_id')) { + context.handle( + _gameIdMeta, + gameId.isAcceptableOrUnknown(data['game_id']!, _gameIdMeta), + ); + } else if (isInserting) { + context.missing(_gameIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, gameId}; + @override + StatisticGameTableData map(Map data, {String? tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGameTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + gameId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}game_id'], + )!, + ); + } + + @override + $StatisticGameTableTable createAlias(String alias) { + return $StatisticGameTableTable(attachedDatabase, alias); + } +} + +class StatisticGameTableData extends DataClass + implements Insertable { + final String statisticId; + final String gameId; + const StatisticGameTableData({ + required this.statisticId, + required this.gameId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['game_id'] = Variable(gameId); + return map; + } + + StatisticGameTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGameTableCompanion( + statisticId: Value(statisticId), + gameId: Value(gameId), + ); + } + + factory StatisticGameTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGameTableData( + statisticId: serializer.fromJson(json['statisticId']), + gameId: serializer.fromJson(json['gameId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'gameId': serializer.toJson(gameId), + }; + } + + StatisticGameTableData copyWith({String? statisticId, String? gameId}) => + StatisticGameTableData( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + ); + StatisticGameTableData copyWithCompanion(StatisticGameTableCompanion data) { + return StatisticGameTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + gameId: data.gameId.present ? data.gameId.value : this.gameId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableData(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, gameId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGameTableData && + other.statisticId == this.statisticId && + other.gameId == this.gameId); +} + +class StatisticGameTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value gameId; + final Value rowid; + const StatisticGameTableCompanion({ + this.statisticId = const Value.absent(), + this.gameId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGameTableCompanion.insert({ + required String statisticId, + required String gameId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + gameId = Value(gameId); + static Insertable custom({ + Expression? statisticId, + Expression? gameId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (gameId != null) 'game_id': gameId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGameTableCompanion copyWith({ + Value? statisticId, + Value? gameId, + Value? rowid, + }) { + return StatisticGameTableCompanion( + statisticId: statisticId ?? this.statisticId, + gameId: gameId ?? this.gameId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (gameId.present) { + map['game_id'] = Variable(gameId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGameTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('gameId: $gameId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + +class $StatisticGroupTableTable extends StatisticGroupTable + with TableInfo<$StatisticGroupTableTable, StatisticGroupTableData> { + @override + final GeneratedDatabase attachedDatabase; + final String? _alias; + $StatisticGroupTableTable(this.attachedDatabase, [this._alias]); + static const VerificationMeta _statisticIdMeta = const VerificationMeta( + 'statisticId', + ); + @override + late final GeneratedColumn statisticId = GeneratedColumn( + 'statistic_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES statistic_table (id) ON DELETE CASCADE', + ), + ); + static const VerificationMeta _groupIdMeta = const VerificationMeta( + 'groupId', + ); + @override + late final GeneratedColumn groupId = GeneratedColumn( + 'group_id', + aliasedName, + false, + type: DriftSqlType.string, + requiredDuringInsert: true, + defaultConstraints: GeneratedColumn.constraintIsAlways( + 'REFERENCES group_table (id) ON DELETE CASCADE', + ), + ); + @override + List get $columns => [statisticId, groupId]; + @override + String get aliasedName => _alias ?? actualTableName; + @override + String get actualTableName => $name; + static const String $name = 'statistic_group_table'; + @override + VerificationContext validateIntegrity( + Insertable instance, { + bool isInserting = false, + }) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('statistic_id')) { + context.handle( + _statisticIdMeta, + statisticId.isAcceptableOrUnknown( + data['statistic_id']!, + _statisticIdMeta, + ), + ); + } else if (isInserting) { + context.missing(_statisticIdMeta); + } + if (data.containsKey('group_id')) { + context.handle( + _groupIdMeta, + groupId.isAcceptableOrUnknown(data['group_id']!, _groupIdMeta), + ); + } else if (isInserting) { + context.missing(_groupIdMeta); + } + return context; + } + + @override + Set get $primaryKey => {statisticId, groupId}; + @override + StatisticGroupTableData map( + Map data, { + String? tablePrefix, + }) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; + return StatisticGroupTableData( + statisticId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}statistic_id'], + )!, + groupId: attachedDatabase.typeMapping.read( + DriftSqlType.string, + data['${effectivePrefix}group_id'], + )!, + ); + } + + @override + $StatisticGroupTableTable createAlias(String alias) { + return $StatisticGroupTableTable(attachedDatabase, alias); + } +} + +class StatisticGroupTableData extends DataClass + implements Insertable { + final String statisticId; + final String groupId; + const StatisticGroupTableData({ + required this.statisticId, + required this.groupId, + }); + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + map['statistic_id'] = Variable(statisticId); + map['group_id'] = Variable(groupId); + return map; + } + + StatisticGroupTableCompanion toCompanion(bool nullToAbsent) { + return StatisticGroupTableCompanion( + statisticId: Value(statisticId), + groupId: Value(groupId), + ); + } + + factory StatisticGroupTableData.fromJson( + Map json, { + ValueSerializer? serializer, + }) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return StatisticGroupTableData( + statisticId: serializer.fromJson(json['statisticId']), + groupId: serializer.fromJson(json['groupId']), + ); + } + @override + Map toJson({ValueSerializer? serializer}) { + serializer ??= driftRuntimeOptions.defaultSerializer; + return { + 'statisticId': serializer.toJson(statisticId), + 'groupId': serializer.toJson(groupId), + }; + } + + StatisticGroupTableData copyWith({String? statisticId, String? groupId}) => + StatisticGroupTableData( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + ); + StatisticGroupTableData copyWithCompanion(StatisticGroupTableCompanion data) { + return StatisticGroupTableData( + statisticId: data.statisticId.present + ? data.statisticId.value + : this.statisticId, + groupId: data.groupId.present ? data.groupId.value : this.groupId, + ); + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableData(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId') + ..write(')')) + .toString(); + } + + @override + int get hashCode => Object.hash(statisticId, groupId); + @override + bool operator ==(Object other) => + identical(this, other) || + (other is StatisticGroupTableData && + other.statisticId == this.statisticId && + other.groupId == this.groupId); +} + +class StatisticGroupTableCompanion + extends UpdateCompanion { + final Value statisticId; + final Value groupId; + final Value rowid; + const StatisticGroupTableCompanion({ + this.statisticId = const Value.absent(), + this.groupId = const Value.absent(), + this.rowid = const Value.absent(), + }); + StatisticGroupTableCompanion.insert({ + required String statisticId, + required String groupId, + this.rowid = const Value.absent(), + }) : statisticId = Value(statisticId), + groupId = Value(groupId); + static Insertable custom({ + Expression? statisticId, + Expression? groupId, + Expression? rowid, + }) { + return RawValuesInsertable({ + if (statisticId != null) 'statistic_id': statisticId, + if (groupId != null) 'group_id': groupId, + if (rowid != null) 'rowid': rowid, + }); + } + + StatisticGroupTableCompanion copyWith({ + Value? statisticId, + Value? groupId, + Value? rowid, + }) { + return StatisticGroupTableCompanion( + statisticId: statisticId ?? this.statisticId, + groupId: groupId ?? this.groupId, + rowid: rowid ?? this.rowid, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (statisticId.present) { + map['statistic_id'] = Variable(statisticId.value); + } + if (groupId.present) { + map['group_id'] = Variable(groupId.value); + } + if (rowid.present) { + map['rowid'] = Variable(rowid.value); + } + return map; + } + + @override + String toString() { + return (StringBuffer('StatisticGroupTableCompanion(') + ..write('statisticId: $statisticId, ') + ..write('groupId: $groupId, ') + ..write('rowid: $rowid') + ..write(')')) + .toString(); + } +} + abstract class _$AppDatabase extends GeneratedDatabase { _$AppDatabase(QueryExecutor e) : super(e); $AppDatabaseManager get managers => $AppDatabaseManager(this); @@ -2749,6 +3763,13 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final $ScoreEntryTableTable scoreEntryTable = $ScoreEntryTableTable( this, ); + late final $StatisticTableTable statisticTable = $StatisticTableTable(this); + late final $StatisticScopeTableTable statisticScopeTable = + $StatisticScopeTableTable(this); + late final $StatisticGameTableTable statisticGameTable = + $StatisticGameTableTable(this); + late final $StatisticGroupTableTable statisticGroupTable = + $StatisticGroupTableTable(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); @@ -2761,6 +3782,16 @@ abstract class _$AppDatabase extends GeneratedDatabase { late final GameDao gameDao = GameDao(this as AppDatabase); late final ScoreEntryDao scoreEntryDao = ScoreEntryDao(this as AppDatabase); late final TeamDao teamDao = TeamDao(this as AppDatabase); + late final StatisticDao statisticDao = StatisticDao(this as AppDatabase); + late final StatisticScopeDao statisticScopeDao = StatisticScopeDao( + this as AppDatabase, + ); + late final StatisticGameDao statisticGameDao = StatisticGameDao( + this as AppDatabase, + ); + late final StatisticGroupDao statisticGroupDao = StatisticGroupDao( + this as AppDatabase, + ); @override Iterable> get allTables => allSchemaEntities.whereType>(); @@ -2774,6 +3805,10 @@ abstract class _$AppDatabase extends GeneratedDatabase { teamTable, playerMatchTable, scoreEntryTable, + statisticTable, + statisticScopeTable, + statisticGameTable, + statisticGroupTable, ]; @override StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ @@ -2840,6 +3875,41 @@ abstract class _$AppDatabase extends GeneratedDatabase { ), result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)], ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_scope_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'game_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_game_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'statistic_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), + WritePropagation( + on: TableUpdateQuery.onTableName( + 'group_table', + limitUpdateKind: UpdateKind.delete, + ), + result: [TableUpdate('statistic_group_table', kind: UpdateKind.delete)], + ), ]); } @@ -3419,6 +4489,33 @@ final class $$GroupTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.groupTable.id, + db.statisticGroupTable.groupId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.groupId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GroupTableTableFilterComposer @@ -3499,6 +4596,31 @@ class $$GroupTableTableFilterComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableOrderingComposer @@ -3603,6 +4725,32 @@ class $$GroupTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.groupId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GroupTableTableTableManager @@ -3621,6 +4769,7 @@ class $$GroupTableTableTableManager PrefetchHooks Function({ bool matchTableRefs, bool playerGroupTableRefs, + bool statisticGroupTableRefs, }) > { $$GroupTableTableTableManager(_$AppDatabase db, $GroupTableTable table) @@ -3671,12 +4820,17 @@ class $$GroupTableTableTableManager ) .toList(), prefetchHooksCallback: - ({matchTableRefs = false, playerGroupTableRefs = false}) { + ({ + matchTableRefs = false, + playerGroupTableRefs = false, + statisticGroupTableRefs = false, + }) { return PrefetchHooks( db: db, explicitlyWatchedTables: [ if (matchTableRefs) db.matchTable, if (playerGroupTableRefs) db.playerGroupTable, + if (statisticGroupTableRefs) db.statisticGroupTable, ], addJoins: null, getPrefetchedDataCallback: (items) async { @@ -3723,6 +4877,27 @@ class $$GroupTableTableTableManager ), typedResults: items, ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + GroupTableData, + $GroupTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$GroupTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GroupTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.groupId == item.id, + ), + typedResults: items, + ), ]; }, ); @@ -3743,7 +4918,11 @@ typedef $$GroupTableTableProcessedTableManager = $$GroupTableTableUpdateCompanionBuilder, (GroupTableData, $$GroupTableTableReferences), GroupTableData, - PrefetchHooks Function({bool matchTableRefs, bool playerGroupTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool playerGroupTableRefs, + bool statisticGroupTableRefs, + }) >; typedef $$GameTableTableCreateCompanionBuilder = GameTableCompanion Function({ @@ -3789,6 +4968,33 @@ final class $$GameTableTableReferences manager.$state.copyWith(prefetchedData: cache), ); } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.gameTable.id, + db.statisticGameTable.gameId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.gameId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } } class $$GameTableTableFilterComposer @@ -3859,6 +5065,31 @@ class $$GameTableTableFilterComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableOrderingComposer @@ -3962,6 +5193,32 @@ class $$GameTableTableAnnotationComposer ); return f(composer); } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.gameId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } } class $$GameTableTableTableManager @@ -3977,7 +5234,10 @@ class $$GameTableTableTableManager $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({ + bool matchTableRefs, + bool statisticGameTableRefs, + }) > { $$GameTableTableTableManager(_$AppDatabase db, $GameTableTable table) : super( @@ -4038,36 +5298,63 @@ class $$GameTableTableTableManager ), ) .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, - ), - ]; + prefetchHooksCallback: + ({matchTableRefs = false, statisticGameTableRefs = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (matchTableRefs) db.matchTable, + if (statisticGameTableRefs) db.statisticGameTable, + ], + 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, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + GameTableData, + $GameTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$GameTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$GameTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.gameId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); }, - ); - }, ), ); } @@ -4084,7 +5371,7 @@ typedef $$GameTableTableProcessedTableManager = $$GameTableTableUpdateCompanionBuilder, (GameTableData, $$GameTableTableReferences), GameTableData, - PrefetchHooks Function({bool matchTableRefs}) + PrefetchHooks Function({bool matchTableRefs, bool statisticGameTableRefs}) >; typedef $$MatchTableTableCreateCompanionBuilder = MatchTableCompanion Function({ @@ -6273,6 +7560,1557 @@ typedef $$ScoreEntryTableTableProcessedTableManager = ScoreEntryTableData, PrefetchHooks Function({bool playerId, bool matchId}) >; +typedef $$StatisticTableTableCreateCompanionBuilder = + StatisticTableCompanion Function({ + required String id, + required String type, + Value timeframe, + Value displayCount, + Value rowid, + }); +typedef $$StatisticTableTableUpdateCompanionBuilder = + StatisticTableCompanion Function({ + Value id, + Value type, + Value timeframe, + Value displayCount, + Value rowid, + }); + +final class $$StatisticTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData + > { + $$StatisticTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static MultiTypedResultKey< + $StatisticScopeTableTable, + List + > + _statisticScopeTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticScopeTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticScopeTable.statisticId, + ), + ); + + $$StatisticScopeTableTableProcessedTableManager get statisticScopeTableRefs { + final manager = $$StatisticScopeTableTableTableManager( + $_db, + $_db.statisticScopeTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticScopeTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGameTableTable, + List + > + _statisticGameTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGameTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGameTable.statisticId, + ), + ); + + $$StatisticGameTableTableProcessedTableManager get statisticGameTableRefs { + final manager = $$StatisticGameTableTableTableManager( + $_db, + $_db.statisticGameTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGameTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } + + static MultiTypedResultKey< + $StatisticGroupTableTable, + List + > + _statisticGroupTableRefsTable(_$AppDatabase db) => + MultiTypedResultKey.fromTable( + db.statisticGroupTable, + aliasName: $_aliasNameGenerator( + db.statisticTable.id, + db.statisticGroupTable.statisticId, + ), + ); + + $$StatisticGroupTableTableProcessedTableManager get statisticGroupTableRefs { + final manager = $$StatisticGroupTableTableTableManager( + $_db, + $_db.statisticGroupTable, + ).filter((f) => f.statisticId.id.sqlEquals($_itemColumn('id')!)); + + final cache = $_typedResult.readTableOrNull( + _statisticGroupTableRefsTable($_db), + ); + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: cache), + ); + } +} + +class $$StatisticTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableFilterComposer({ + 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 type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnFilters(column), + ); + + ColumnFilters get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnFilters(column), + ); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableFilterComposer f) f, + ) { + final $$StatisticScopeTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableFilterComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableFilterComposer f) f, + ) { + final $$StatisticGameTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableFilterComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableFilterComposer f) f, + ) { + final $$StatisticGroupTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableFilterComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableOrderingComposer({ + 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 type => $composableBuilder( + column: $table.type, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get timeframe => $composableBuilder( + column: $table.timeframe, + builder: (column) => ColumnOrderings(column), + ); + + ColumnOrderings get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => ColumnOrderings(column), + ); +} + +class $$StatisticTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticTableTable> { + $$StatisticTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get id => + $composableBuilder(column: $table.id, builder: (column) => column); + + GeneratedColumn get type => + $composableBuilder(column: $table.type, builder: (column) => column); + + GeneratedColumn get timeframe => + $composableBuilder(column: $table.timeframe, builder: (column) => column); + + GeneratedColumn get displayCount => $composableBuilder( + column: $table.displayCount, + builder: (column) => column, + ); + + Expression statisticScopeTableRefs( + Expression Function($$StatisticScopeTableTableAnnotationComposer a) f, + ) { + final $$StatisticScopeTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticScopeTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticScopeTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticScopeTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGameTableRefs( + Expression Function($$StatisticGameTableTableAnnotationComposer a) f, + ) { + final $$StatisticGameTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGameTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGameTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGameTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } + + Expression statisticGroupTableRefs( + Expression Function($$StatisticGroupTableTableAnnotationComposer a) f, + ) { + final $$StatisticGroupTableTableAnnotationComposer composer = + $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.id, + referencedTable: $db.statisticGroupTable, + getReferencedColumn: (t) => t.statisticId, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticGroupTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticGroupTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return f(composer); + } +} + +class $$StatisticTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + > { + $$StatisticTableTableTableManager( + _$AppDatabase db, + $StatisticTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticTableTableAnnotationComposer($db: db, $table: table), + updateCompanionCallback: + ({ + Value id = const Value.absent(), + Value type = const Value.absent(), + Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion( + id: id, + type: type, + timeframe: timeframe, + displayCount: displayCount, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String id, + required String type, + Value timeframe = const Value.absent(), + Value displayCount = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticTableCompanion.insert( + id: id, + type: type, + timeframe: timeframe, + displayCount: displayCount, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: + ({ + statisticScopeTableRefs = false, + statisticGameTableRefs = false, + statisticGroupTableRefs = false, + }) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [ + if (statisticScopeTableRefs) db.statisticScopeTable, + if (statisticGameTableRefs) db.statisticGameTable, + if (statisticGroupTableRefs) db.statisticGroupTable, + ], + addJoins: null, + getPrefetchedDataCallback: (items) async { + return [ + if (statisticScopeTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticScopeTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticScopeTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticScopeTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGameTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGameTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGameTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGameTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + if (statisticGroupTableRefs) + await $_getPrefetchedData< + StatisticTableData, + $StatisticTableTable, + StatisticGroupTableData + >( + currentTable: table, + referencedTable: $$StatisticTableTableReferences + ._statisticGroupTableRefsTable(db), + managerFromTypedResult: (p0) => + $$StatisticTableTableReferences( + db, + table, + p0, + ).statisticGroupTableRefs, + referencedItemsForCurrentItem: + (item, referencedItems) => referencedItems.where( + (e) => e.statisticId == item.id, + ), + typedResults: items, + ), + ]; + }, + ); + }, + ), + ); +} + +typedef $$StatisticTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticTableTable, + StatisticTableData, + $$StatisticTableTableFilterComposer, + $$StatisticTableTableOrderingComposer, + $$StatisticTableTableAnnotationComposer, + $$StatisticTableTableCreateCompanionBuilder, + $$StatisticTableTableUpdateCompanionBuilder, + (StatisticTableData, $$StatisticTableTableReferences), + StatisticTableData, + PrefetchHooks Function({ + bool statisticScopeTableRefs, + bool statisticGameTableRefs, + bool statisticGroupTableRefs, + }) + >; +typedef $$StatisticScopeTableTableCreateCompanionBuilder = + StatisticScopeTableCompanion Function({ + required String statisticId, + required String scope, + Value rowid, + }); +typedef $$StatisticScopeTableTableUpdateCompanionBuilder = + StatisticScopeTableCompanion Function({ + Value statisticId, + Value scope, + Value rowid, + }); + +final class $$StatisticScopeTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData + > { + $$StatisticScopeTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticScopeTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } +} + +class $$StatisticScopeTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnFilters get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnFilters(column), + ); + + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + ColumnOrderings get scope => $composableBuilder( + column: $table.scope, + builder: (column) => ColumnOrderings(column), + ); + + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticScopeTableTable> { + $$StatisticScopeTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + GeneratedColumn get scope => + $composableBuilder(column: $table.scope, builder: (column) => column); + + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } +} + +class $$StatisticScopeTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + > { + $$StatisticScopeTableTableTableManager( + _$AppDatabase db, + $StatisticScopeTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticScopeTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticScopeTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticScopeTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value scope = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String scope, + Value rowid = const Value.absent(), + }) => StatisticScopeTableCompanion.insert( + statisticId: statisticId, + scope: scope, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticScopeTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticScopeTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticScopeTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticScopeTableTable, + StatisticScopeTableData, + $$StatisticScopeTableTableFilterComposer, + $$StatisticScopeTableTableOrderingComposer, + $$StatisticScopeTableTableAnnotationComposer, + $$StatisticScopeTableTableCreateCompanionBuilder, + $$StatisticScopeTableTableUpdateCompanionBuilder, + (StatisticScopeTableData, $$StatisticScopeTableTableReferences), + StatisticScopeTableData, + PrefetchHooks Function({bool statisticId}) + >; +typedef $$StatisticGameTableTableCreateCompanionBuilder = + StatisticGameTableCompanion Function({ + required String statisticId, + required String gameId, + Value rowid, + }); +typedef $$StatisticGameTableTableUpdateCompanionBuilder = + StatisticGameTableCompanion Function({ + Value statisticId, + Value gameId, + Value rowid, + }); + +final class $$StatisticGameTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData + > { + $$StatisticGameTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGameTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GameTableTable _gameIdTable(_$AppDatabase db) => + db.gameTable.createAlias( + $_aliasNameGenerator(db.statisticGameTable.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]), + ); + } +} + +class $$StatisticGameTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$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; + } +} + +class $$StatisticGameTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$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; + } +} + +class $$StatisticGameTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGameTableTable> { + $$StatisticGameTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, + joinBuilder: joinBuilder, + $removeJoinBuilderFromRootComposer: + $removeJoinBuilderFromRootComposer, + ), + ); + return composer; + } + + $$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; + } +} + +class $$StatisticGameTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + > { + $$StatisticGameTableTableTableManager( + _$AppDatabase db, + $StatisticGameTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGameTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGameTableTableOrderingComposer($db: db, $table: table), + createComputedFieldComposer: () => + $$StatisticGameTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value gameId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String gameId, + Value rowid = const Value.absent(), + }) => StatisticGameTableCompanion.insert( + statisticId: statisticId, + gameId: gameId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGameTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, gameId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGameTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (gameId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.gameId, + referencedTable: + $$StatisticGameTableTableReferences + ._gameIdTable(db), + referencedColumn: + $$StatisticGameTableTableReferences + ._gameIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGameTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGameTableTable, + StatisticGameTableData, + $$StatisticGameTableTableFilterComposer, + $$StatisticGameTableTableOrderingComposer, + $$StatisticGameTableTableAnnotationComposer, + $$StatisticGameTableTableCreateCompanionBuilder, + $$StatisticGameTableTableUpdateCompanionBuilder, + (StatisticGameTableData, $$StatisticGameTableTableReferences), + StatisticGameTableData, + PrefetchHooks Function({bool statisticId, bool gameId}) + >; +typedef $$StatisticGroupTableTableCreateCompanionBuilder = + StatisticGroupTableCompanion Function({ + required String statisticId, + required String groupId, + Value rowid, + }); +typedef $$StatisticGroupTableTableUpdateCompanionBuilder = + StatisticGroupTableCompanion Function({ + Value statisticId, + Value groupId, + Value rowid, + }); + +final class $$StatisticGroupTableTableReferences + extends + BaseReferences< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData + > { + $$StatisticGroupTableTableReferences( + super.$_db, + super.$_table, + super.$_typedResult, + ); + + static $StatisticTableTable _statisticIdTable(_$AppDatabase db) => + db.statisticTable.createAlias( + $_aliasNameGenerator( + db.statisticGroupTable.statisticId, + db.statisticTable.id, + ), + ); + + $$StatisticTableTableProcessedTableManager get statisticId { + final $_column = $_itemColumn('statistic_id')!; + + final manager = $$StatisticTableTableTableManager( + $_db, + $_db.statisticTable, + ).filter((f) => f.id.sqlEquals($_column)); + final item = $_typedResult.readTableOrNull(_statisticIdTable($_db)); + if (item == null) return manager; + return ProcessedTableManager( + manager.$state.copyWith(prefetchedData: [item]), + ); + } + + static $GroupTableTable _groupIdTable(_$AppDatabase db) => + db.groupTable.createAlias( + $_aliasNameGenerator(db.statisticGroupTable.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]), + ); + } +} + +class $$StatisticGroupTableTableFilterComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableFilterComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableFilterComposer get statisticId { + final $$StatisticTableTableFilterComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableFilterComposer( + $db: $db, + $table: $db.statisticTable, + $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; + } +} + +class $$StatisticGroupTableTableOrderingComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableOrderingComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableOrderingComposer get statisticId { + final $$StatisticTableTableOrderingComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableOrderingComposer( + $db: $db, + $table: $db.statisticTable, + $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 $$StatisticGroupTableTableAnnotationComposer + extends Composer<_$AppDatabase, $StatisticGroupTableTable> { + $$StatisticGroupTableTableAnnotationComposer({ + required super.$db, + required super.$table, + super.joinBuilder, + super.$addJoinBuilderToRootComposer, + super.$removeJoinBuilderFromRootComposer, + }); + $$StatisticTableTableAnnotationComposer get statisticId { + final $$StatisticTableTableAnnotationComposer composer = $composerBuilder( + composer: this, + getCurrentColumn: (t) => t.statisticId, + referencedTable: $db.statisticTable, + getReferencedColumn: (t) => t.id, + builder: + ( + joinBuilder, { + $addJoinBuilderToRootComposer, + $removeJoinBuilderFromRootComposer, + }) => $$StatisticTableTableAnnotationComposer( + $db: $db, + $table: $db.statisticTable, + $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; + } +} + +class $$StatisticGroupTableTableTableManager + extends + RootTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + > { + $$StatisticGroupTableTableTableManager( + _$AppDatabase db, + $StatisticGroupTableTable table, + ) : super( + TableManagerState( + db: db, + table: table, + createFilteringComposer: () => + $$StatisticGroupTableTableFilterComposer($db: db, $table: table), + createOrderingComposer: () => + $$StatisticGroupTableTableOrderingComposer( + $db: db, + $table: table, + ), + createComputedFieldComposer: () => + $$StatisticGroupTableTableAnnotationComposer( + $db: db, + $table: table, + ), + updateCompanionCallback: + ({ + Value statisticId = const Value.absent(), + Value groupId = const Value.absent(), + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + createCompanionCallback: + ({ + required String statisticId, + required String groupId, + Value rowid = const Value.absent(), + }) => StatisticGroupTableCompanion.insert( + statisticId: statisticId, + groupId: groupId, + rowid: rowid, + ), + withReferenceMapper: (p0) => p0 + .map( + (e) => ( + e.readTable(table), + $$StatisticGroupTableTableReferences(db, table, e), + ), + ) + .toList(), + prefetchHooksCallback: ({statisticId = false, groupId = false}) { + return PrefetchHooks( + db: db, + explicitlyWatchedTables: [], + addJoins: + < + T extends TableManagerState< + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic, + dynamic + > + >(state) { + if (statisticId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.statisticId, + referencedTable: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._statisticIdTable(db) + .id, + ) + as T; + } + if (groupId) { + state = + state.withJoin( + currentTable: table, + currentColumn: table.groupId, + referencedTable: + $$StatisticGroupTableTableReferences + ._groupIdTable(db), + referencedColumn: + $$StatisticGroupTableTableReferences + ._groupIdTable(db) + .id, + ) + as T; + } + + return state; + }, + getPrefetchedDataCallback: (items) async { + return []; + }, + ); + }, + ), + ); +} + +typedef $$StatisticGroupTableTableProcessedTableManager = + ProcessedTableManager< + _$AppDatabase, + $StatisticGroupTableTable, + StatisticGroupTableData, + $$StatisticGroupTableTableFilterComposer, + $$StatisticGroupTableTableOrderingComposer, + $$StatisticGroupTableTableAnnotationComposer, + $$StatisticGroupTableTableCreateCompanionBuilder, + $$StatisticGroupTableTableUpdateCompanionBuilder, + (StatisticGroupTableData, $$StatisticGroupTableTableReferences), + StatisticGroupTableData, + PrefetchHooks Function({bool statisticId, bool groupId}) + >; class $AppDatabaseManager { final _$AppDatabase _db; @@ -6293,4 +9131,12 @@ class $AppDatabaseManager { $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); $$ScoreEntryTableTableTableManager get scoreEntryTable => $$ScoreEntryTableTableTableManager(_db, _db.scoreEntryTable); + $$StatisticTableTableTableManager get statisticTable => + $$StatisticTableTableTableManager(_db, _db.statisticTable); + $$StatisticScopeTableTableTableManager get statisticScopeTable => + $$StatisticScopeTableTableTableManager(_db, _db.statisticScopeTable); + $$StatisticGameTableTableTableManager get statisticGameTable => + $$StatisticGameTableTableTableManager(_db, _db.statisticGameTable); + $$StatisticGroupTableTableTableManager get statisticGroupTable => + $$StatisticGroupTableTableTableManager(_db, _db.statisticGroupTable); } diff --git a/lib/data/db/tables/statistic_game_table.dart b/lib/data/db/tables/statistic_game_table.dart new file mode 100644 index 0000000..e1cc7d4 --- /dev/null +++ b/lib/data/db/tables/statistic_game_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/game_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGameTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get gameId => + text().references(GameTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, gameId}; +} diff --git a/lib/data/db/tables/statistic_group_table.dart b/lib/data/db/tables/statistic_group_table.dart new file mode 100644 index 0000000..cd642ad --- /dev/null +++ b/lib/data/db/tables/statistic_group_table.dart @@ -0,0 +1,13 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/group_table.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticGroupTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get groupId => + text().references(GroupTable, #id, onDelete: KeyAction.cascade)(); + + @override + Set> get primaryKey => {statisticId, groupId}; +} diff --git a/lib/data/db/tables/statistic_scope_table.dart b/lib/data/db/tables/statistic_scope_table.dart new file mode 100644 index 0000000..3a9bcdc --- /dev/null +++ b/lib/data/db/tables/statistic_scope_table.dart @@ -0,0 +1,11 @@ +import 'package:drift/drift.dart'; +import 'package:tallee/data/db/tables/statistic_table.dart'; + +class StatisticScopeTable extends Table { + TextColumn get statisticId => + text().references(StatisticTable, #id, onDelete: KeyAction.cascade)(); + TextColumn get scope => text()(); + + @override + Set> get primaryKey => {statisticId, scope}; +} diff --git a/lib/data/db/tables/statistic_table.dart b/lib/data/db/tables/statistic_table.dart new file mode 100644 index 0000000..ef368a5 --- /dev/null +++ b/lib/data/db/tables/statistic_table.dart @@ -0,0 +1,11 @@ +import 'package:drift/drift.dart'; + +class StatisticTable extends Table { + TextColumn get id => text()(); + TextColumn get type => text()(); + TextColumn get timeframe => text().nullable()(); + IntColumn get displayCount => integer().withDefault(const Constant(5))(); + + @override + Set> get primaryKey => {id}; +} diff --git a/lib/data/models/game.dart b/lib/data/models/game.dart index 89bbd30..ec69204 100644 --- a/lib/data/models/game.dart +++ b/lib/data/models/game.dart @@ -8,13 +8,13 @@ class Game { final String name; final Ruleset ruleset; final String description; - final GameColor color; + final AppColor color; final String icon; Game({ required this.name, required this.ruleset, - this.color = GameColor.orange, + this.color = AppColor.orange, this.description = '', this.icon = '', String? id, @@ -33,7 +33,7 @@ class Game { String? name, Ruleset? ruleset, String? description, - GameColor? color, + AppColor? color, String? icon, }) { return Game( @@ -73,7 +73,7 @@ class Game { orElse: () => Ruleset.singleWinner, ), description = json['description'], - color = GameColor.values.firstWhere((e) => e.name == json['color']), + color = AppColor.values.firstWhere((e) => e.name == json['color']), icon = json['icon']; Map toJson() => { diff --git a/lib/data/models/group.dart b/lib/data/models/group.dart index 5c1515c..8b1fe92 100644 --- a/lib/data/models/group.dart +++ b/lib/data/models/group.dart @@ -5,17 +5,17 @@ import 'package:uuid/uuid.dart'; class Group { final String id; - final String name; - final String description; final DateTime createdAt; + final String name; final List members; + final String description; Group({ + required this.name, + required this.members, String? id, DateTime? createdAt, - required this.name, String? description, - required this.members, }) : id = id ?? const Uuid().v4(), createdAt = createdAt ?? clock.now(), description = description ?? ''; diff --git a/lib/data/models/match.dart b/lib/data/models/match.dart index 2c43fe3..601a01c 100644 --- a/lib/data/models/match.dart +++ b/lib/data/models/match.dart @@ -107,7 +107,7 @@ class Match { name: '', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group = null, diff --git a/lib/data/models/statistic.dart b/lib/data/models/statistic.dart new file mode 100644 index 0000000..e995d93 --- /dev/null +++ b/lib/data/models/statistic.dart @@ -0,0 +1,48 @@ +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:uuid/uuid.dart'; + +class Statistic { + final String id; + final StatisticType type; + final List scopes; + final Timeframe? timeframe; + final List? selectedGroups; + final List? selectedGames; + final int displayCount; + + Statistic({ + required this.type, + required this.scopes, + this.timeframe, + this.selectedGroups, + this.selectedGames, + this.displayCount = 5, + String? id, + }) : id = id ?? const Uuid().v4(); + + @override + String toString() { + return 'Statistic(id: $id, type: $type, scopes: $scopes, timeframe: $timeframe, selectedGroups: $selectedGroups, selectedGames: $selectedGames)'; + } + + Statistic copyWith({ + StatisticType? type, + List? scopes, + Timeframe? timeframe, + List? selectedGroups, + List? selectedGames, + int? displayCount, + }) { + return Statistic( + id: id, + type: type ?? this.type, + scopes: scopes ?? this.scopes, + timeframe: timeframe ?? this.timeframe, + selectedGroups: selectedGroups ?? this.selectedGroups, + selectedGames: selectedGames ?? this.selectedGames, + displayCount: displayCount ?? this.displayCount, + ); + } +} diff --git a/lib/l10n/arb/app_de.arb b/lib/l10n/arb/app_de.arb index 610d2c9..7dc32f6 100644 --- a/lib/l10n/arb/app_de.arb +++ b/lib/l10n/arb/app_de.arb @@ -2,14 +2,18 @@ "@@locale": "de", "all_players": "Alle Spieler:innen", "all_players_selected": "Alle Spieler:innen ausgewählt", + "all_time": "Gesamter Zeitraum", "amount_of_matches": "Anzahl der Spiele", "app_name": "Tallee", + "average_score": "Durchschnittliche Punktzahl", "best_player": "Beste:r Spieler:in", + "best_score": "Beste Punktzahl", "cancel": "Abbrechen", "choose_color": "Farbe wählen", "choose_game": "Spielvorlage wählen", "choose_group": "Gruppe wählen", "choose_ruleset": "Regelwerk wählen", + "classifier": "Klassifikator", "color": "Farbe", "color_blue": "Blau", "color_green": "Grün", @@ -21,11 +25,29 @@ "color_yellow": "Gelb", "confirm": "Bestätigen", "could_not_add_player": "Spieler:in {playerName} konnte nicht hinzugefügt werden", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Spielvorlage erstellen", "create_group": "Gruppe erstellen", "create_match": "Spiel erstellen", "create_new_group": "Neue Gruppe erstellen", "create_new_match": "Neues Spiel erstellen", + "create_statistic": "Statistik erstellen", + "create_statistic_classifier_subtitle": "Wähle die anzuzeigende Hauptmetrik aus", + "create_statistic_classifier_title": "Klassifikator", + "create_statistic_games_subtitle": "Wähle die gefilterten Spielvorlagen", + "create_statistic_games_title": "Spielvorlagen", + "create_statistic_groups_subtitle": "Wähle die gefilterten Gruppen", + "create_statistic_groups_title": "Gruppen", + "create_statistic_scope_subtitle": "Wähle den Hauptfilter für deine Statistik. Er bestimmt, welche Daten zur Berechnung des Klassifikators verwendet werden.", + "create_statistic_scope_title": "Bereich", + "create_statistic_timeframe_subtitle": "Wähle einen Zeitraum, nach dem die Daten gefiltert werden. Nur Spiele, die innerhalb des Zeitraums beendet wurden, fließen in die Statistik ein.", + "create_statistic_timeframe_title": "Zeitraum", "created_on": "Erstellt am", "data": "Daten", "data_successfully_deleted": "Daten erfolgreich gelöscht", @@ -47,6 +69,7 @@ "delete_match": "Spiel löschen", "delete_player": "Spieler:in löschen", "description": "Beschreibung", + "displayed_entries": "Angezeigte Einträge", "drag_to_set_placement": "Ziehen um Platzierung zu setzen", "edit_game": "Spielvorlage bearbeiten", "edit_group": "Gruppe bearbeiten", @@ -63,9 +86,11 @@ "exit_view": "Ansicht verlassen", "export_canceled": "Export abgebrochen", "export_data": "Daten exportieren", + "filter": "Filter", "format_exception": "Formatfehler (siehe Konsole)", "game": "Spielvorlage", "game_name": "Spielvorlagenname", + "games": "Spielvorlagen", "group": "Gruppe", "group_name": "Gruppenname", "group_profile": "Gruppenprofil", @@ -77,11 +102,17 @@ "import_data": "Daten importieren", "info": "Info", "invalid_schema": "Ungültiges Schema", + "last_180_days": "Letzte 180 Tage", + "last_30_days": "Letzte 30 Tage", + "last_7_days": "Letzte 7 Tage", + "last_90_days": "Letzte 90 Tage", + "last_year": "Letztes Jahr", "least_points": "Niedrigste Punkte", "legal": "Rechtliches", "legal_notice": "Impressum", "licenses": "Lizenzen", "live_edit_mode": "Live-Bearbeitungsmodus", + "loading": "Lädt...", "loser": "Verlierer:in", "lowest_score": "Niedrigste Punkte", "match_in_progress": "Spiel läuft...", @@ -108,6 +139,7 @@ "no_results_entered_yet": "Noch keine Ergebnisse eingetragen", "no_second_match_available": "Kein zweites Spiel verfügbar", "no_statistics_available": "Keine Statistiken verfügbar", + "no_statistics_created_yet": "Noch keine Statistiken erstellt", "none": "Kein", "none_group": "Keine", "not_available": "Nicht verfügbar", @@ -132,16 +164,36 @@ "ruleset_single_loser": "Genau ein:e Verlierer:in wird bestimmt; der letzte Platz erhält die Strafe oder Konsequenz.", "ruleset_single_winner": "Genau ein:e Gewinner:in wird gewählt; Unentschieden werden durch einen vordefinierten Tie-Breaker aufgelöst.", "save_changes": "Änderungen speichern", + "scope": "Bereich", "search_for_groups": "Nach Gruppen suchen", "search_for_players": "Nach Spieler:innen suchen", + "select_a_classifier": "Klassifikator auswählen", + "select_a_game": "Spielvorlage auswählen", + "select_a_group": "Gruppe auswählen", + "select_a_scope": "Bereich auswählen", + "select_a_timeframe": "Zeitraum auswählen", + "select_a_timeframe_for_which_data_will_be_filtered": "Wähle einen Zeitraum, für den die Daten gefiltert werden sollen", "select_loser": "Verlierer:in wählen", + "select_the_filtered_games": "Wähle Spiele, nach denen gefiltert werden soll.", + "select_the_filtered_groups": "Wähle Gruppen, nach denen gefiltert werden soll.", + "select_the_filtered_timeframe": "Wähle einen Zeitraum, nach dem gefiltert werden soll.", "select_winner": "Gewinner:in wählen", "select_winners": "Gewinner:innen wählen", + "selected_games": "Ausgewählte Spielvorlagen", + "selected_groups": "Ausgewählte Gruppen", "selected_players": "Ausgewählte Spieler:innen", "set_name": "Name setzen", "settings": "Einstellungen", "single_loser": "Ein:e Verlierer:in", "single_winner": "Ein:e Gewinner:in", + "statistic_type_average_score": "Durchschnittliche Punktzahl", + "statistic_type_best_score": "Beste Punktzahl", + "statistic_type_total_losses": "Niederlagen insgesamt", + "statistic_type_total_matches": "Spiele insgesamt", + "statistic_type_total_score": "Punktzahl insgesamt", + "statistic_type_total_wins": "Siege insgesamt", + "statistic_type_winrate": "Siegquote", + "statistic_type_worst_score": "Schlechteste Punktzahl", "statistics": "Statistiken", "stats": "Statistiken", "successfully_added_player": "Spieler:in {playerName} erfolgreich hinzugefügt", @@ -149,12 +201,18 @@ "there_is_no_group_matching_your_search": "Es gibt keine Gruppe, die deiner Suche entspricht", "this_cannot_be_undone": "Dies kann nicht rückgängig gemacht werden.", "tie": "Unentschieden", + "timeframe": "Zeitraum", "today_at": "Heute um", + "total_losses": "Niederlagen insgesamt", + "total_matches": "Spiele insgesamt", + "total_score": "Punktzahl insgesamt", + "total_wins": "Siege insgesamt", "undo": "Rückgängig", "unknown_exception": "Unbekannter Fehler (siehe Konsole)", "winner": "Gewinner:in", "winners": "Gewinner:innen", "winrate": "Siegquote", "wins": "Siege", + "worst_score": "Schlechteste Punktzahl", "yesterday_at": "Gestern um" } \ No newline at end of file diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index a8b0634..988433b 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -18,14 +18,30 @@ "color_purple": "Purple", "color_red": "Red", "color_teal": "Teal", + "displayed_entries": "Displayed entries", "color_yellow": "Yellow", "confirm": "Confirm", - "could_not_add_player": "Could not add player", + "could_not_add_player": "Could not add player {playerName}", + "@could_not_add_player": { + "placeholders": { + "playerName": { + "type": "String" + } + } + }, "create_game": "Create Game", "create_group": "Create Group", "create_match": "Create match", "create_new_group": "Create new group", "create_new_match": "Create new match", + "create_statistic": "Create statistic", + "classifier": "Classifier", + "select_the_filtered_timeframe": "Select the timeframe you want to filter by.", + "select_the_filtered_games": "Select the games you want to filter by.", + "games": "Games", + "select_the_filtered_groups": "Select the groups you want to filter by.", + "scope": "Scope", + "timeframe": "Timeframe", "created_on": "Created on", "data": "Data", "data_successfully_deleted": "Data successfully deleted", @@ -43,6 +59,7 @@ } } }, + "filter": "Filter", "delete_group": "Delete Group", "delete_match": "Delete Match", "delete_player": "Delete player?", @@ -82,6 +99,7 @@ "legal_notice": "Legal Notice", "licenses": "Licenses", "live_edit_mode": "Live Edit Mode", + "loading": "Loading...", "loser": "Loser", "lowest_score": "Lowest Score", "match_in_progress": "Match in progress...", @@ -108,6 +126,7 @@ "no_results_entered_yet": "No results entered yet", "no_second_match_available": "No second match available", "no_statistics_available": "No statistics available", + "no_statistics_created_yet": "No statistics created yet", "none": "None", "none_group": "None", "not_available": "Not available", @@ -139,10 +158,24 @@ "selected_players": "Selected players", "set_name": "Set name", "settings": "Settings", + "select_a_classifier": "Select a classifier", + "select_a_game": "Select a game", + "select_a_group": "Select a group", + "select_a_scope": "Select a scope", + "select_a_timeframe": "Select a timeframe", "single_loser": "Single Loser", "single_winner": "Single Winner", "statistics": "Statistics", "stats": "Stats", + "selected_games": "Selected games", + "selected_groups": "Selected groups", + "average_score": "Average score", + "best_score": "Best score", + "total_losses": "Total losses", + "total_matches": "Total matches", + "total_score": "Total score", + "total_wins": "Total wins", + "worst_score": "Worst score", "successfully_added_player": "Successfully added player {playerName}", "@successfully_added_player": { "description": "Success message when adding a player", @@ -157,6 +190,12 @@ "there_is_no_group_matching_your_search": "There is no group matching your search", "this_cannot_be_undone": "This can't be undone.", "tie": "Tie", + "all_time": "All time", + "last_180_days": "Last 180 days", + "last_30_days": "Last 30 days", + "last_7_days": "Last 7 days", + "last_90_days": "Last 90 days", + "last_year": "Last year", "today_at": "Today at", "undo": "Undo", "unknown_exception": "Unknown Exception (see console)", diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart index 4cee263..30e1b33 100644 --- a/lib/l10n/generated/app_localizations.dart +++ b/lib/l10n/generated/app_localizations.dart @@ -206,6 +206,12 @@ abstract class AppLocalizations { /// **'Teal'** String get color_teal; + /// No description provided for @displayed_entries. + /// + /// In en, this message translates to: + /// **'Displayed entries'** + String get displayed_entries; + /// No description provided for @color_yellow. /// /// In en, this message translates to: @@ -221,8 +227,8 @@ abstract class AppLocalizations { /// No description provided for @could_not_add_player. /// /// In en, this message translates to: - /// **'Could not add player'** - String could_not_add_player(Object playerName); + /// **'Could not add player {playerName}'** + String could_not_add_player(String playerName); /// No description provided for @create_game. /// @@ -254,6 +260,54 @@ abstract class AppLocalizations { /// **'Create new match'** String get create_new_match; + /// No description provided for @create_statistic. + /// + /// In en, this message translates to: + /// **'Create statistic'** + String get create_statistic; + + /// No description provided for @classifier. + /// + /// In en, this message translates to: + /// **'Classifier'** + String get classifier; + + /// No description provided for @select_the_filtered_timeframe. + /// + /// In en, this message translates to: + /// **'Select the timeframe you want to filter by.'** + String get select_the_filtered_timeframe; + + /// No description provided for @select_the_filtered_games. + /// + /// In en, this message translates to: + /// **'Select the games you want to filter by.'** + String get select_the_filtered_games; + + /// No description provided for @games. + /// + /// In en, this message translates to: + /// **'Games'** + String get games; + + /// No description provided for @select_the_filtered_groups. + /// + /// In en, this message translates to: + /// **'Select the groups you want to filter by.'** + String get select_the_filtered_groups; + + /// No description provided for @scope. + /// + /// In en, this message translates to: + /// **'Scope'** + String get scope; + + /// No description provided for @timeframe. + /// + /// In en, this message translates to: + /// **'Timeframe'** + String get timeframe; + /// No description provided for @created_on. /// /// In en, this message translates to: @@ -314,6 +368,12 @@ abstract class AppLocalizations { /// **'If you delete this game template, {count, plural, =1{1 match} other{{count} matches}} using this game template will also be deleted.'** String delete_game_with_matches_warning(int count); + /// No description provided for @filter. + /// + /// In en, this message translates to: + /// **'Filter'** + String get filter; + /// No description provided for @delete_group. /// /// In en, this message translates to: @@ -548,6 +608,12 @@ abstract class AppLocalizations { /// **'Live Edit Mode'** String get live_edit_mode; + /// No description provided for @loading. + /// + /// In en, this message translates to: + /// **'Loading...'** + String get loading; + /// No description provided for @loser. /// /// In en, this message translates to: @@ -704,6 +770,12 @@ abstract class AppLocalizations { /// **'No statistics available'** String get no_statistics_available; + /// No description provided for @no_statistics_created_yet. + /// + /// In en, this message translates to: + /// **'No statistics created yet'** + String get no_statistics_created_yet; + /// No description provided for @none. /// /// In en, this message translates to: @@ -890,6 +962,36 @@ abstract class AppLocalizations { /// **'Settings'** String get settings; + /// No description provided for @select_a_classifier. + /// + /// In en, this message translates to: + /// **'Select a classifier'** + String get select_a_classifier; + + /// No description provided for @select_a_game. + /// + /// In en, this message translates to: + /// **'Select a game'** + String get select_a_game; + + /// No description provided for @select_a_group. + /// + /// In en, this message translates to: + /// **'Select a group'** + String get select_a_group; + + /// No description provided for @select_a_scope. + /// + /// In en, this message translates to: + /// **'Select a scope'** + String get select_a_scope; + + /// No description provided for @select_a_timeframe. + /// + /// In en, this message translates to: + /// **'Select a timeframe'** + String get select_a_timeframe; + /// No description provided for @single_loser. /// /// In en, this message translates to: @@ -914,6 +1016,60 @@ abstract class AppLocalizations { /// **'Stats'** String get stats; + /// No description provided for @selected_games. + /// + /// In en, this message translates to: + /// **'Selected games'** + String get selected_games; + + /// No description provided for @selected_groups. + /// + /// In en, this message translates to: + /// **'Selected groups'** + String get selected_groups; + + /// No description provided for @average_score. + /// + /// In en, this message translates to: + /// **'Average score'** + String get average_score; + + /// No description provided for @best_score. + /// + /// In en, this message translates to: + /// **'Best score'** + String get best_score; + + /// No description provided for @total_losses. + /// + /// In en, this message translates to: + /// **'Total losses'** + String get total_losses; + + /// No description provided for @total_matches. + /// + /// In en, this message translates to: + /// **'Total matches'** + String get total_matches; + + /// No description provided for @total_score. + /// + /// In en, this message translates to: + /// **'Total score'** + String get total_score; + + /// No description provided for @total_wins. + /// + /// In en, this message translates to: + /// **'Total wins'** + String get total_wins; + + /// No description provided for @worst_score. + /// + /// In en, this message translates to: + /// **'Worst score'** + String get worst_score; + /// Success message when adding a player /// /// In en, this message translates to: @@ -944,6 +1100,42 @@ abstract class AppLocalizations { /// **'Tie'** String get tie; + /// No description provided for @all_time. + /// + /// In en, this message translates to: + /// **'All time'** + String get all_time; + + /// No description provided for @last_180_days. + /// + /// In en, this message translates to: + /// **'Last 180 days'** + String get last_180_days; + + /// No description provided for @last_30_days. + /// + /// In en, this message translates to: + /// **'Last 30 days'** + String get last_30_days; + + /// No description provided for @last_7_days. + /// + /// In en, this message translates to: + /// **'Last 7 days'** + String get last_7_days; + + /// No description provided for @last_90_days. + /// + /// In en, this message translates to: + /// **'Last 90 days'** + String get last_90_days; + + /// No description provided for @last_year. + /// + /// In en, this message translates to: + /// **'Last year'** + String get last_year; + /// No description provided for @today_at. /// /// 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 66dca88..1417bb7 100644 --- a/lib/l10n/generated/app_localizations_de.dart +++ b/lib/l10n/generated/app_localizations_de.dart @@ -62,6 +62,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get color_teal => 'Türkis'; + @override + String get displayed_entries => 'Angezeigte Einträge'; + @override String get color_yellow => 'Gelb'; @@ -69,7 +72,7 @@ class AppLocalizationsDe extends AppLocalizations { String get confirm => 'Bestätigen'; @override - String could_not_add_player(Object playerName) { + String could_not_add_player(String playerName) { return 'Spieler:in $playerName konnte nicht hinzugefügt werden'; } @@ -88,6 +91,33 @@ class AppLocalizationsDe extends AppLocalizations { @override String get create_new_match => 'Neues Spiel erstellen'; + @override + String get create_statistic => 'Statistik erstellen'; + + @override + String get classifier => 'Klassifikator'; + + @override + String get select_the_filtered_timeframe => + 'Wähle einen Zeitraum, nach dem gefiltert werden soll.'; + + @override + String get select_the_filtered_games => + 'Wähle Spiele, nach denen gefiltert werden soll.'; + + @override + String get games => 'Spielvorlagen'; + + @override + String get select_the_filtered_groups => + 'Wähle Gruppen, nach denen gefiltert werden soll.'; + + @override + String get scope => 'Bereich'; + + @override + String get timeframe => 'Zeitraum'; + @override String get created_on => 'Erstellt am'; @@ -128,6 +158,9 @@ class AppLocalizationsDe extends AppLocalizations { return 'Wenn du diese Spielvorlage löschst, $_temp0 mit dieser Spielvorlage ebenfalls gelöscht.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Gruppe löschen'; @@ -249,6 +282,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get live_edit_mode => 'Live-Bearbeitungsmodus'; + @override + String get loading => 'Lädt...'; + @override String get loser => 'Verlierer:in'; @@ -328,6 +364,9 @@ class AppLocalizationsDe extends AppLocalizations { @override String get no_statistics_available => 'Keine Statistiken verfügbar'; + @override + String get no_statistics_created_yet => 'Noch keine Statistiken erstellt'; + @override String get none => 'Kein'; @@ -426,6 +465,21 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settings => 'Einstellungen'; + @override + String get select_a_classifier => 'Klassifikator auswählen'; + + @override + String get select_a_game => 'Spielvorlage auswählen'; + + @override + String get select_a_group => 'Gruppe auswählen'; + + @override + String get select_a_scope => 'Bereich auswählen'; + + @override + String get select_a_timeframe => 'Zeitraum auswählen'; + @override String get single_loser => 'Ein:e Verlierer:in'; @@ -438,6 +492,33 @@ class AppLocalizationsDe extends AppLocalizations { @override String get stats => 'Statistiken'; + @override + String get selected_games => 'Ausgewählte Spielvorlagen'; + + @override + String get selected_groups => 'Ausgewählte Gruppen'; + + @override + String get average_score => 'Durchschnittliche Punktzahl'; + + @override + String get best_score => 'Beste Punktzahl'; + + @override + String get total_losses => 'Niederlagen insgesamt'; + + @override + String get total_matches => 'Spiele insgesamt'; + + @override + String get total_score => 'Punktzahl insgesamt'; + + @override + String get total_wins => 'Siege insgesamt'; + + @override + String get worst_score => 'Schlechteste Punktzahl'; + @override String successfully_added_player(String playerName) { return 'Spieler:in $playerName erfolgreich hinzugefügt'; @@ -458,6 +539,24 @@ class AppLocalizationsDe extends AppLocalizations { @override String get tie => 'Unentschieden'; + @override + String get all_time => 'Gesamter Zeitraum'; + + @override + String get last_180_days => 'Letzte 180 Tage'; + + @override + String get last_30_days => 'Letzte 30 Tage'; + + @override + String get last_7_days => 'Letzte 7 Tage'; + + @override + String get last_90_days => 'Letzte 90 Tage'; + + @override + String get last_year => 'Letztes Jahr'; + @override String get today_at => 'Heute um'; diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart index b9e467a..2275b97 100644 --- a/lib/l10n/generated/app_localizations_en.dart +++ b/lib/l10n/generated/app_localizations_en.dart @@ -62,6 +62,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get color_teal => 'Teal'; + @override + String get displayed_entries => 'Displayed entries'; + @override String get color_yellow => 'Yellow'; @@ -69,8 +72,8 @@ class AppLocalizationsEn extends AppLocalizations { String get confirm => 'Confirm'; @override - String could_not_add_player(Object playerName) { - return 'Could not add player'; + String could_not_add_player(String playerName) { + return 'Could not add player $playerName'; } @override @@ -88,6 +91,33 @@ class AppLocalizationsEn extends AppLocalizations { @override String get create_new_match => 'Create new match'; + @override + String get create_statistic => 'Create statistic'; + + @override + String get classifier => 'Classifier'; + + @override + String get select_the_filtered_timeframe => + 'Select the timeframe you want to filter by.'; + + @override + String get select_the_filtered_games => + 'Select the games you want to filter by.'; + + @override + String get games => 'Games'; + + @override + String get select_the_filtered_groups => + 'Select the groups you want to filter by.'; + + @override + String get scope => 'Scope'; + + @override + String get timeframe => 'Timeframe'; + @override String get created_on => 'Created on'; @@ -128,6 +158,9 @@ class AppLocalizationsEn extends AppLocalizations { return 'If you delete this game template, $_temp0 using this game template will also be deleted.'; } + @override + String get filter => 'Filter'; + @override String get delete_group => 'Delete Group'; @@ -249,6 +282,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get live_edit_mode => 'Live Edit Mode'; + @override + String get loading => 'Loading...'; + @override String get loser => 'Loser'; @@ -328,6 +364,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get no_statistics_available => 'No statistics available'; + @override + String get no_statistics_created_yet => 'No statistics created yet'; + @override String get none => 'None'; @@ -426,6 +465,21 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings => 'Settings'; + @override + String get select_a_classifier => 'Select a classifier'; + + @override + String get select_a_game => 'Select a game'; + + @override + String get select_a_group => 'Select a group'; + + @override + String get select_a_scope => 'Select a scope'; + + @override + String get select_a_timeframe => 'Select a timeframe'; + @override String get single_loser => 'Single Loser'; @@ -438,6 +492,33 @@ class AppLocalizationsEn extends AppLocalizations { @override String get stats => 'Stats'; + @override + String get selected_games => 'Selected games'; + + @override + String get selected_groups => 'Selected groups'; + + @override + String get average_score => 'Average score'; + + @override + String get best_score => 'Best score'; + + @override + String get total_losses => 'Total losses'; + + @override + String get total_matches => 'Total matches'; + + @override + String get total_score => 'Total score'; + + @override + String get total_wins => 'Total wins'; + + @override + String get worst_score => 'Worst score'; + @override String successfully_added_player(String playerName) { return 'Successfully added player $playerName'; @@ -457,6 +538,24 @@ class AppLocalizationsEn extends AppLocalizations { @override String get tie => 'Tie'; + @override + String get all_time => 'All time'; + + @override + String get last_180_days => 'Last 180 days'; + + @override + String get last_30_days => 'Last 30 days'; + + @override + String get last_7_days => 'Last 7 days'; + + @override + String get last_90_days => 'Last 90 days'; + + @override + String get last_year => 'Last year'; + @override String get today_at => 'Today at'; diff --git a/lib/presentation/views/main_menu/custom_navigation_bar.dart b/lib/presentation/views/main_menu/custom_navigation_bar.dart index 7e5434b..07d66b4 100644 --- a/lib/presentation/views/main_menu/custom_navigation_bar.dart +++ b/lib/presentation/views/main_menu/custom_navigation_bar.dart @@ -6,7 +6,7 @@ import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/views/main_menu/group_view/group_view.dart'; import 'package:tallee/presentation/views/main_menu/match_view/match_view.dart'; import 'package:tallee/presentation/views/main_menu/settings_view/settings_view.dart'; -import 'package:tallee/presentation/views/main_menu/statistics_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistics_view.dart'; import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; import 'package:tallee/presentation/widgets/navbar_item.dart'; diff --git a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart index 3c51cab..4d085d7 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/choose_game_view.dart @@ -164,7 +164,7 @@ class _ChooseGameViewState extends State { game.ruleset, context, ), - badgeColor: getColorFromGameColor(game.color), + badgeColor: getColorFromAppColor(game.color), isHighlighted: selectedGameId == game.id, onTap: () async { setState(() { diff --git a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart index 998f4e1..0671055 100644 --- a/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart +++ b/lib/presentation/views/main_menu/match_view/create_match/create_game_view.dart @@ -49,10 +49,10 @@ class _CreateGameViewState extends State { late final AppDatabase db; late List<(Ruleset, String)> _rulesets; - late List<(GameColor, String)> _colors; + late List<(AppColor, String)> _colors; Ruleset? selectedRuleset = Ruleset.singleWinner; - GameColor? selectedColor = GameColor.orange; + AppColor? selectedColor = AppColor.orange; /// Controller for the game name input field. final _gameNameController = TextEditingController(); @@ -87,10 +87,10 @@ class _CreateGameViewState extends State { ), ); _colors = List.generate( - GameColor.values.length, + AppColor.values.length, (index) => ( - GameColor.values[index], - translateGameColorToString(GameColor.values[index], context), + AppColor.values[index], + translateAppColorToString(AppColor.values[index], context), ), ); @@ -117,7 +117,6 @@ class _CreateGameViewState extends State { return ScaffoldMessenger( child: Scaffold( - backgroundColor: CustomTheme.backgroundColor, appBar: AppBar( title: Text(isEditing ? loc.edit_game : loc.create_game), actions: [ @@ -468,7 +467,7 @@ class _CreateGameViewState extends State { height: 16, margin: const EdgeInsets.only(left: 12), decoration: BoxDecoration( - color: getColorFromGameColor( + color: getColorFromAppColor( _colors[index].$1, ), shape: BoxShape.circle, @@ -502,13 +501,13 @@ class _CreateGameViewState extends State { width: 16, height: 16, decoration: BoxDecoration( - color: getColorFromGameColor(selectedColor!), + color: getColorFromAppColor(selectedColor!), shape: BoxShape.circle, ), ), Padding( padding: const EdgeInsets.only(right: 5), - child: Text(translateGameColorToString(selectedColor!, context)), + child: Text(translateAppColorToString(selectedColor!, context)), ), Transform.rotate( angle: pi / 2, 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 83ff069..1d30afb 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -39,7 +39,7 @@ class _MatchViewState extends State { game: Game( name: 'Game name', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: '', ), group: Group( @@ -79,7 +79,7 @@ class _MatchViewState extends State { visible: matches.isNotEmpty, replacement: Center( child: TopCenteredMessage( - icon: Icons.report, + icon: Icons.info, title: loc.info, message: loc.no_matches_created_yet, ), diff --git a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart index 591b7fe..9bd4e0b 100644 --- a/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart +++ b/lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart @@ -34,7 +34,6 @@ const allDependencies = [ _cli_util, _clock, _code_assets, - _code_builder, _collection, _convert, _coverage, @@ -154,6 +153,7 @@ const allDependencies = [ _source_map_stack_trace, _source_maps, _source_span, + _sqlcipher_flutter_libs, _sqlite3, _sqlite3_flutter_libs, _sqlparser, @@ -670,17 +670,17 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// build_runner 2.13.1 +/// build_runner 2.15.0 const _build_runner = Package( name: 'build_runner', description: 'A build system for Dart code generation and modular compilation.', repository: 'https://github.com/dart-lang/build/tree/master/build_runner', authors: [], - version: '2.13.1', + version: '2.15.0', spdxIdentifiers: ['BSD-3-Clause'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('code_builder'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')], + dependencies: [PackageRef('analyzer'), PackageRef('args'), PackageRef('async'), PackageRef('build'), PackageRef('build_config'), PackageRef('build_daemon'), PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('convert'), PackageRef('crypto'), PackageRef('dart_style'), PackageRef('glob'), PackageRef('graphs'), PackageRef('http_multi_server'), PackageRef('io'), PackageRef('json_annotation'), PackageRef('logging'), PackageRef('meta'), PackageRef('mime'), PackageRef('package_config'), PackageRef('path'), PackageRef('pool'), PackageRef('pub_semver'), PackageRef('shelf'), PackageRef('shelf_web_socket'), PackageRef('stream_transform'), PackageRef('watcher'), PackageRef('web_socket_channel'), PackageRef('yaml')], devDependencies: [PackageRef('stream_channel'), PackageRef('test')], license: '''Copyright 2016, the Dart project authors. @@ -1510,47 +1510,6 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// code_builder 4.11.1 -const _code_builder = Package( - name: 'code_builder', - description: 'A fluent, builder-based library for generating valid Dart code.', - repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/code_builder', - authors: [], - version: '4.11.1', - spdxIdentifiers: ['BSD-3-Clause'], - isMarkdown: false, - isSdk: false, - dependencies: [PackageRef('built_collection'), PackageRef('built_value'), PackageRef('collection'), PackageRef('matcher'), PackageRef('meta')], - devDependencies: [PackageRef('build'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('source_gen'), PackageRef('test')], - license: '''Copyright 2016, the Dart project authors. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - * Neither the name of Google LLC nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', - ); - /// collection 1.19.1 const _collection = Package( name: 'collection', @@ -2581,14 +2540,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift 2.31.0 +/// drift 2.33.0 const _drift = Package( name: 'drift', description: 'Drift is a reactive library to store relational data in Dart and Flutter applications.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '2.31.0', + version: '2.33.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -2617,14 +2576,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift_dev 2.31.0 +/// drift_dev 2.33.0 const _drift_dev = Package( name: 'drift_dev', description: 'Dev-dependency for users of drift. Contains the generator and development tools.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '2.31.0', + version: '2.33.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -2653,18 +2612,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// drift_flutter 0.2.8 +/// drift_flutter 0.3.0 const _drift_flutter = Package( name: 'drift_flutter', description: 'Easily set up drift databases across platforms in Flutter apps.', homepage: 'https://drift.simonbinder.eu/', repository: 'https://github.com/simolus3/drift', authors: [], - version: '0.2.8', + version: '0.3.0', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs')], + dependencies: [PackageRef('drift'), PackageRef('flutter'), PackageRef('meta'), PackageRef('path'), PackageRef('path_provider'), PackageRef('sqlite3'), PackageRef('sqlite3_flutter_libs'), PackageRef('sqlcipher_flutter_libs')], devDependencies: [PackageRef('build_runner'), PackageRef('drift_dev'), PackageRef('lints'), PackageRef('test'), PackageRef('flutter_test'), PackageRef('async')], license: '''MIT License @@ -3057,18 +3016,18 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// file_saver 0.3.1 +/// file_saver 0.4.0 const _file_saver = Package( name: 'file_saver', - description: 'A Flutter plugin for saving files across all platforms (Android, iOS, Web, Windows, macOS, Linux). Save files from bytes, File objects, file paths, or download from URLs with a single method call. Features include MIME type support, Dio integration, and platform-specific save locations. Supports saveAs() dialog for user-selected locations on supported platforms.', + description: 'Save files from bytes, paths, streams, and URLs across Android, iOS, Web, Windows, macOS, and Linux.', homepage: 'https://hassanansari.dev', repository: 'https://github.com/incrediblezayed/file_saver', authors: [], - version: '0.3.1', + version: '0.4.0', spdxIdentifiers: ['BSD-3-Clause'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('path_provider_linux'), PackageRef('path_provider_windows'), PackageRef('web')], + dependencies: [PackageRef('collection'), PackageRef('dio'), PackageRef('flutter'), PackageRef('flutter_web_plugins'), PackageRef('path_provider'), PackageRef('web')], devDependencies: [PackageRef('flutter_lints'), PackageRef('flutter_test')], license: '''BSD 3-Clause License @@ -37683,18 +37642,264 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', ); -/// sqlite3 2.9.4 +/// sqlcipher_flutter_libs 0.7.0+eol +const _sqlcipher_flutter_libs = Package( + name: 'sqlcipher_flutter_libs', + description: 'Not used anymore, update to version 3.x of package:sqlite3 instead', + homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlcipher_flutter_libs', + authors: [], + version: '0.7.0+eol', + spdxIdentifiers: ['Pixar', 'MIT', 'BSD-3-Clause-HP'], + isMarkdown: false, + isSdk: false, + dependencies: [], + devDependencies: [], + license: '''sqlcipher_flutter_libs + +MIT License + +Copyright (c) 2020 Simon Binder + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +-------------------------------------------------------------------------------- +OpenSSL + + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + +-------------------------------------------------------------------------------- +SQLCipher + +Copyright (c) 2008-2020 Zetetic LLC +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of the ZETETIC LLC nor the + names of its contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY ZETETIC LLC ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL ZETETIC LLC BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', + ); + +/// sqlite3 3.3.1 const _sqlite3 = Package( name: 'sqlite3', description: 'Provides lightweight yet convenient bindings to SQLite by using dart:ffi', homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3', authors: [], - version: '2.9.4', + version: '3.3.1', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data')], - devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert')], + dependencies: [PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('path'), PackageRef('web'), PackageRef('typed_data'), PackageRef('hooks'), PackageRef('code_assets'), PackageRef('native_toolchain_c'), PackageRef('crypto')], + devDependencies: [PackageRef('analyzer'), PackageRef('build_daemon'), PackageRef('build_runner'), PackageRef('dart_style'), PackageRef('http'), PackageRef('lints'), PackageRef('shelf'), PackageRef('shelf_static'), PackageRef('stream_channel'), PackageRef('test'), PackageRef('pub_semver'), PackageRef('convert'), PackageRef('package_config'), PackageRef('logging')], license: '''MIT License Copyright (c) 2020 Simon Binder @@ -37718,17 +37923,17 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// sqlite3_flutter_libs 0.5.42 +/// sqlite3_flutter_libs 0.6.0+eol const _sqlite3_flutter_libs = Package( name: 'sqlite3_flutter_libs', - description: 'Flutter plugin to include native sqlite3 libraries with your app', - homepage: 'https://github.com/simolus3/sqlite3.dart/tree/v2/sqlite3_flutter_libs', + description: 'Not used anymore, update to version 3.x of package:sqlite3 instead', + homepage: 'https://github.com/simolus3/sqlite3.dart/tree/main/legacy/sqlite3_flutter_libs', authors: [], - version: '0.5.42', + version: '0.6.0+eol', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, - dependencies: [PackageRef('flutter')], + dependencies: [], devDependencies: [], license: '''MIT License @@ -37753,14 +37958,14 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// sqlparser 0.43.1 +/// sqlparser 0.44.4 const _sqlparser = Package( name: 'sqlparser', description: 'Parses sqlite statements and performs static analysis on them', homepage: 'https://github.com/simolus3/drift/tree/develop/sqlparser', repository: 'https://github.com/simolus3/drift', authors: [], - version: '0.43.1', + version: '0.44.4', spdxIdentifiers: ['MIT'], isMarkdown: false, isSdk: false, @@ -39415,12 +39620,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.''', ); -/// tallee 0.0.33+273 +/// tallee 0.0.35+277 const _tallee = Package( name: 'tallee', description: 'Tracking App for Card Games', authors: [], - version: '0.0.33+273', + version: '0.0.35+277', spdxIdentifiers: ['LGPL-3.0'], isMarkdown: false, isSdk: false, diff --git a/lib/presentation/views/main_menu/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view.dart deleted file mode 100644 index 8659a2e..0000000 --- a/lib/presentation/views/main_menu/statistics_view.dart +++ /dev/null @@ -1,311 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; -import 'package:tallee/core/constants.dart'; -import 'package:tallee/data/db/database.dart'; -import 'package:tallee/data/models/match.dart'; -import 'package:tallee/data/models/player.dart'; -import 'package:tallee/l10n/generated/app_localizations.dart'; -import 'package:tallee/presentation/widgets/app_skeleton.dart'; -import 'package:tallee/presentation/widgets/tiles/quick_info_tile.dart'; -import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; -import 'package:tallee/presentation/widgets/top_centered_message.dart'; - -class StatisticsView extends StatefulWidget { - /// A view that displays player statistics - const StatisticsView({super.key}); - - @override - State createState() => _StatisticsViewState(); -} - -class _StatisticsViewState extends State { - int matchCount = 0; - int groupCount = 0; - - List<(Player, int)> winCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, int)> matchCounts = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - List<(Player, double)> winRates = List.filled(6, ( - Player(name: 'Skeleton Player'), - 1, - )); - bool isLoading = true; - - @override - void initState() { - super.initState(); - loadStatisticData(); - } - - @override - Widget build(BuildContext context) { - final loc = AppLocalizations.of(context); - return LayoutBuilder( - builder: (BuildContext context, BoxConstraints constraints) { - return SingleChildScrollView( - child: AppSkeleton( - enabled: isLoading, - fixLayoutBuilder: true, - child: ConstrainedBox( - constraints: BoxConstraints(minWidth: constraints.maxWidth), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.matches, - icon: Icons.groups_rounded, - value: matchCount, - ), - SizedBox(width: constraints.maxWidth * 0.05), - QuickInfoTile( - width: constraints.maxWidth * 0.45, - height: constraints.maxHeight * 0.13, - title: loc.groups, - icon: Icons.groups_rounded, - value: groupCount, - ), - ], - ), - SizedBox(height: constraints.maxHeight * 0.02), - Visibility( - visible: - winCounts.isEmpty && - matchCounts.isEmpty && - winRates.isEmpty, - replacement: Column( - children: [ - StatisticsTile( - icon: Icons.sports_score, - title: loc.wins, - width: constraints.maxWidth * 0.95, - values: winCounts, - itemCount: 3, - barColor: Colors.green, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.percent, - title: loc.winrate, - width: constraints.maxWidth * 0.95, - values: winRates, - itemCount: 5, - barColor: Colors.orange[700]!, - ), - SizedBox(height: constraints.maxHeight * 0.02), - StatisticsTile( - icon: Icons.casino, - title: loc.amount_of_matches, - width: constraints.maxWidth * 0.95, - values: matchCounts, - itemCount: 10, - barColor: Colors.blue, - ), - ], - ), - child: TopCenteredMessage( - icon: Icons.info, - title: loc.info, - message: AppLocalizations.of( - context, - ).no_statistics_available, - ), - ), - SizedBox(height: MediaQuery.paddingOf(context).bottom), - ], - ), - ), - ), - ); - }, - ); - } - - /// Loads matches and players from the database - /// and calculates statistics for each player - void loadStatisticData() { - final db = Provider.of(context, listen: false); - - Future.wait([ - db.matchDao.getAllMatches(), - db.playerDao.getAllPlayers(), - db.matchDao.getMatchCount(), - db.groupDao.getGroupCount(), - Future.delayed(Constants.MINIMUM_SKELETON_DURATION), - ]).then((results) async { - if (!mounted) return; - - final matches = results[0] as List; - final players = results[1] as List; - matchCount = results[2] as int; - groupCount = results[3] as int; - - winCounts = _calculateWinsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - matchCounts = _calculateMatchAmountsForAllPlayers( - matches: matches, - players: players, - context: context, - ); - winRates = computeWinRatePercent( - winCounts: winCounts, - matchCounts: matchCounts, - ); - - setState(() { - isLoading = false; - }); - }); - } - - /// Calculates the number of wins for each player - /// and returns a sorted list of tuples (playerName, winCount) - List<(Player, int)> _calculateWinsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> winCounts = []; - final loc = AppLocalizations.of(context); - - // Getting the winners - for (var match in matches) { - final mvps = match.mvp; - for (var winner in mvps) { - final index = winCounts.indexWhere((entry) => entry.$1.id == winner.id); - // -1 means winner not found in winCounts - if (index != -1) { - final current = winCounts[index].$2; - winCounts[index] = (winner, current + 1); - } else { - winCounts.add((winner, 1)); - } - } - } - - // Adding all players with zero wins - for (var player in players) { - final index = winCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in winCounts - if (index == -1) { - winCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < winCounts.length; i++) { - final playerId = winCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - winCounts[i] = (player, winCounts[i].$2); - } - - winCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return winCounts; - } - - /// Calculates the number of matches played for each player - /// and returns a sorted list of tuples (playerName, matchCount) - List<(Player, int)> _calculateMatchAmountsForAllPlayers({ - required List matches, - required List players, - required BuildContext context, - }) { - List<(Player, int)> matchCounts = []; - final loc = AppLocalizations.of(context); - - // Counting matches for each player - for (var match in matches) { - for (Player player in match.players) { - // Check if the player is already in matchCounts - final index = matchCounts.indexWhere( - (entry) => entry.$1.id == player.id, - ); - - // -1 -> not found - if (index == -1) { - // Add new entry - matchCounts.add((player, 1)); - } else { - // Update existing entry - final currentMatchAmount = matchCounts[index].$2; - matchCounts[index] = (player, currentMatchAmount + 1); - } - } - } - - // Adding all players with zero matches - for (var player in players) { - final index = matchCounts.indexWhere((entry) => entry.$1.id == player.id); - // -1 means player not found in matchCounts - if (index == -1) { - matchCounts.add((player, 0)); - } - } - - // Replace player IDs with names - for (int i = 0; i < matchCounts.length; i++) { - final playerId = matchCounts[i].$1.id; - final player = players.firstWhere( - (p) => p.id == playerId, - orElse: () => Player(id: playerId, name: loc.not_available), - ); - matchCounts[i] = (player, matchCounts[i].$2); - } - - matchCounts.sort((a, b) => b.$2.compareTo(a.$2)); - - return matchCounts; - } - - List<(Player, double)> computeWinRatePercent({ - required List<(Player, int)> winCounts, - required List<(Player, int)> matchCounts, - }) { - final Map winsMap = {for (var e in winCounts) e.$1: e.$2}; - final Map matchesMap = {for (var e in matchCounts) e.$1: e.$2}; - - // Get all unique player names - final player = {...matchesMap.keys}; - - // Calculate win rates - final result = player.map((name) { - final int w = winsMap[name] ?? 0; - final int m = matchesMap[name] ?? 0; - // Calculate percentage and round to 2 decimal places - // Avoid division by zero - final double percent = (m > 0) - ? double.parse(((w / m)).toStringAsFixed(2)) - : 0; - return (name, percent); - }).toList(); - - // Sort the result: first by winrate descending, - // then by wins descending in case of a tie - result.sort((a, b) { - final cmp = b.$2.compareTo(a.$2); - if (cmp != 0) return cmp; - final wa = winsMap[a.$1] ?? 0; - final wb = winsMap[b.$1] ?? 0; - return wb.compareTo(wa); - }); - - return result; - } -} diff --git a/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart new file mode 100644 index 0000000..a88432a --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/create_statistic_view.dart @@ -0,0 +1,635 @@ +import 'package:animated_custom_dropdown/custom_dropdown.dart'; +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/core/common.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/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/widgets/buttons/animated_dialog_button.dart'; + +class CreateStatisticView extends StatefulWidget { + const CreateStatisticView({super.key, required this.onStatisticCreated}); + + final void Function() onStatisticCreated; + + @override + State createState() => _CreateStatisticViewState(); +} + +class _CreateStatisticViewState extends State { + bool isLoading = false; + + /* Data loaded from the database */ + List players = []; + List games = []; + List groups = []; + + /* User selections */ + StatisticType? selectedType; + List selectedScope = []; + List selectedGames = []; + List selectedPlayers = []; + List selectedGroups = []; + Timeframe? selectedTimeframe; + + @override + void initState() { + loadAllData(); + super.initState(); + } + + @override + Widget build(BuildContext context) { + var loc = AppLocalizations.of(context); + + return ScaffoldMessenger( + child: Scaffold( + appBar: AppBar(title: Text(loc.create_statistic)), + body: Stack( + alignment: AlignmentDirectional.center, + children: [ + SingleChildScrollView( + padding: EdgeInsets.only( + bottom: MediaQuery.of(context).padding.bottom + 80, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Classifier title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.classifier, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const Text( + 'description', + textAlign: TextAlign.start, + style: TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + softWrap: true, + ), + ], + ), + ), + + // Classifier selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateStatisticTypeToString(item, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedType, enabled) => Text( + translateStatisticTypeToString(selectedType, context), + style: headerStyle, + ), + hintText: loc.select_a_classifier, + items: StatisticType.values, + decoration: decoration, + onChanged: (value) { + setState(() { + selectedType = value; + }); + }, + ), + ), + + const SizedBox(height: 10), + + // Scope title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.scope, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + const Text( + 'description', + textAlign: TextAlign.start, + style: TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // Scope selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: loc.select_a_scope, + items: StatisticScope.values, + decoration: decoration, + listItemBuilder: + (context, scope, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + translateScopeToString(scope, context), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems + .map((s) => translateScopeToString(s, context)) + .join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedScope = values; + }); + }, + ), + ), + + if (selectedScope.contains(StatisticScope.selectedGames)) ...[ + const SizedBox(height: 10), + + // games title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.games, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_games, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // game selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_game, + items: games, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.end, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + translateRulesetToString( + item.ruleset, + context, + ), + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + + // Check icon + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: const TextStyle( + color: CustomTheme.textColor, + fontWeight: FontWeight.bold, + ), + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List values) { + setState(() { + selectedGames = values; + }); + }, + ), + ), + ], + + if (selectedScope.contains( + StatisticScope.selectedGroups, + )) ...[ + const SizedBox(height: 10), + + // groups title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.groups, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_groups, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown.multiSelect( + enabled: !isLoading, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading ? loc.loading : loc.select_a_group, + items: groups, + decoration: decoration, + listItemBuilder: + (context, item, isSelected, onItemSelect) => Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + // Name + Text(item.name, style: itemStyle), + const SizedBox(width: 12), + + // Ruleset + Text( + ' ${item.members.length.toString()} ${loc.members}', + style: hintStyle.copyWith(fontSize: 12), + ), + ], + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerListBuilder: (context, selectedItems, enabled) => + Text( + selectedItems.map((g) => g.name).join(', '), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onListChanged: (List groups) { + setState(() { + selectedGroups = groups; + }); + }, + ), + ), + ], + + if (selectedScope.contains(StatisticScope.timeframe)) ...[ + const SizedBox(height: 10), + + // timeframe title + Padding( + padding: const EdgeInsetsGeometry.only(left: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.timeframe, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + Text( + loc.select_the_filtered_timeframe, + textAlign: TextAlign.start, + style: const TextStyle( + color: CustomTheme.textColor, + fontSize: 12, + ), + ), + ], + ), + ), + + // groups selection + Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 16, + ), + child: CustomDropdown( + enabled: !isLoading, + excludeSelected: false, + disabledDecoration: disabledDecoration, + closedHeaderPadding: const EdgeInsets.symmetric( + vertical: 16, + horizontal: 16, + ), + hintText: isLoading + ? loc.loading + : loc.select_a_timeframe, + items: Timeframe.values, + decoration: decoration, + listItemBuilder: + (context, timeframe, isSelected, onItemSelect) => + Row( + mainAxisAlignment: + MainAxisAlignment.spaceBetween, + children: [ + Text( + translateTimeframeToString( + timeframe, + context, + ), + style: itemStyle, + ), + if (isSelected) + const Icon( + Icons.check, + color: CustomTheme.textColor, + ), + ], + ), + headerBuilder: (context, selectedTimeframe, enabled) => + Text( + translateTimeframeToString( + selectedTimeframe, + context, + ), + style: headerStyle, + overflow: TextOverflow.ellipsis, + ), + onChanged: (Timeframe? timeframe) { + setState(() { + selectedTimeframe = timeframe; + }); + }, + ), + ), + ], + ], + ), + ), + + // Create statistic button + Positioned( + bottom: MediaQuery.of(context).padding.bottom, + child: AnimatedDialogButton( + buttonConstraints: const BoxConstraints(minWidth: 350), + buttonText: loc.create_statistic, + onPressed: selectedType != null && selectedScope.isNotEmpty + ? () => submitStatistic() + : null, + ), + ), + ], + ), + ), + ); + } + + CustomDropdownDecoration get decoration => CustomDropdownDecoration( + listItemDecoration: const ListItemDecoration( + selectedIconBorder: BorderSide(color: CustomTheme.primaryColor, width: 1), + selectedIconColor: CustomTheme.primaryColor, + highlightColor: CustomTheme.secondaryColor, + splashColor: Colors.transparent, + selectedColor: CustomTheme.onBoxColor, + ), + listItemStyle: itemStyle, + headerStyle: headerStyle, + hintStyle: hintStyle, + closedFillColor: CustomTheme.boxColor, + closedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + expandedFillColor: CustomTheme.boxColor, + expandedBorder: Border.all(color: CustomTheme.boxBorderColor, width: 1), + ); + + CustomDropdownDisabledDecoration get disabledDecoration => + CustomDropdownDisabledDecoration( + fillColor: CustomTheme.boxColor.withAlpha(125), + border: Border.all( + color: CustomTheme.boxBorderColor.withAlpha(125), + width: 1, + ), + headerStyle: disabledHeaderStyle, + hintStyle: disabledHintStyle, + ); + + TextStyle get headerStyle => const TextStyle( + color: CustomTheme.textColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get itemStyle => + const TextStyle(color: CustomTheme.textColor, fontSize: 14); + + TextStyle get hintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + TextStyle get disabledHeaderStyle => const TextStyle( + color: CustomTheme.hintColor, + fontSize: 14, + fontWeight: FontWeight.bold, + ); + + TextStyle get disabledHintStyle => + const TextStyle(color: CustomTheme.hintColor, fontSize: 14); + + Future loadAllData() async { + isLoading = true; + final db = Provider.of(context, listen: false); + + Future.wait([ + db.playerDao.getAllPlayers(), + db.groupDao.getAllGroups(), + db.gameDao.getAllGames(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]) + .then((results) async { + players = results[0]; + groups = results[1]; + games = results[2]; + isLoading = false; + }) + .catchError((error) { + print('Error loading data: $error'); + }); + } + + void submitStatistic() { + final newStatistic = Statistic( + type: selectedType!, + scopes: selectedScope, + timeframe: selectedTimeframe, + selectedGroups: selectedGroups, + selectedGames: selectedGames, + ); + final db = Provider.of(context, listen: false); + db.statisticDao.addStatistic(statistic: newStatistic); + Navigator.of(context).pop(newStatistic); + } +} + +String translateTimeframeToString(Timeframe timeframe, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (timeframe) { + case Timeframe.last7Days: + return loc.last_7_days; + case Timeframe.last30Days: + return loc.last_30_days; + case Timeframe.last90Days: + return loc.last_90_days; + case Timeframe.last180Days: + return loc.last_180_days; + case Timeframe.lastYear: + return loc.last_year; + case Timeframe.allTime: + return loc.all_time; + } +} + +String translateScopeToString(StatisticScope scope, BuildContext context) { + final loc = AppLocalizations.of(context); + switch (scope) { + case StatisticScope.allPlayers: + return loc.all_players; + case StatisticScope.selectedGroups: + return loc.selected_groups; + case StatisticScope.selectedGames: + return loc.selected_games; + case StatisticScope.timeframe: + return loc.timeframe; + } +} + +String translateStatisticTypeToString( + StatisticType type, + BuildContext context, +) { + final loc = AppLocalizations.of(context); + switch (type) { + case StatisticType.totalMatches: + return loc.total_matches; + case StatisticType.totalWins: + return loc.total_wins; + case StatisticType.totalScore: + return loc.total_score; + case StatisticType.totalLosses: + return loc.total_losses; + case StatisticType.averageScore: + return loc.average_score; + case StatisticType.bestScore: + return loc.best_score; + case StatisticType.worstScore: + return loc.worst_score; + case StatisticType.winrate: + return loc.winrate; + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart new file mode 100644 index 0000000..f1bc209 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_detail_view.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:provider/provider.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart'; +import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +class StatisticDetailView extends StatefulWidget { + const StatisticDetailView({ + super.key, + required this.statistic, + required this.values, + required this.icon, + required this.barColor, + }); + + final Statistic statistic; + final List<(Player, num)> values; + final IconData icon; + final Color barColor; + + @override + State createState() => _StatisticDetailViewState(); +} + +class _StatisticDetailViewState extends State { + late int displayCount; + + @override + void initState() { + super.initState(); + displayCount = widget.statistic.displayCount; + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final title = translateStatisticTypeToString( + widget.statistic.type, + context, + ); + const style = TextStyle(fontWeight: FontWeight.bold); + + return Scaffold( + appBar: AppBar( + title: Text(title), + leading: HapticIconButton( + icon: const Icon(Icons.arrow_back_ios_new), + onPressed: () => handleBack(context), + ), + ), + body: SingleChildScrollView( + padding: const EdgeInsets.all(12.0), + child: Column( + children: [ + StatisticsTile( + icon: widget.icon, + title: title, + width: MediaQuery.sizeOf(context).width * 0.95, + values: widget.values, + barColor: widget.barColor, + selectedGroups: widget.statistic.selectedGroups, + selectedGames: widget.statistic.selectedGames, + displayCount: displayCount, + showAllValues: true, + ), + const SizedBox(height: 12), + + InfoTile( + icon: Icons.filter_alt, + title: loc.filter, + content: Column( + spacing: 12, + + children: [ + // Scopes + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.scope, style: style), + Text( + widget.statistic.scopes + .map( + (scope) => translateScopeToString(scope, context), + ) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Timeframe + if (widget.statistic.timeframe != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.timeframe, style: style), + Text( + translateTimeframeToString( + widget.statistic.timeframe!, + context, + ), + textAlign: TextAlign.end, + ), + ], + ), + + // Groups + if (widget.statistic.selectedGroups != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.groups, style: style), + Text( + widget.statistic.selectedGroups! + .map((group) => group.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + // Games + if (widget.statistic.selectedGames != null) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.games, style: style), + Text( + widget.statistic.selectedGames! + .map((game) => game.name) + .join('\n'), + textAlign: TextAlign.end, + ), + ], + ), + + if (widget.values.isNotEmpty) + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text(loc.displayed_entries, style: style), + Row( + children: [ + HapticIconButton( + icon: const Icon(Icons.remove), + onPressed: displayCount <= 1 + ? null + : () => setState(() => displayCount -= 1), + ), + SizedBox( + width: 30, + child: Text( + '$displayCount', + textAlign: TextAlign.center, + ), + ), + HapticIconButton( + icon: const Icon(Icons.add), + onPressed: displayCount >= widget.values.length + ? null + : () => setState(() => displayCount += 1), + ), + ], + ), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } + + // Handles saving the display count and giving it to statistics view + Future handleBack(BuildContext context) async { + final db = Provider.of(context, listen: false); + await db.statisticDao.updateDisplayCount(widget.statistic.id, displayCount); + if (context.mounted) Navigator.of(context).pop(displayCount); + } +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart new file mode 100644 index 0000000..807358e --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart @@ -0,0 +1,322 @@ +import 'dart:math'; + +import 'package:flutter/material.dart'; +import 'package:tallee/core/common.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart' + show translateStatisticTypeToString; +import 'package:tallee/presentation/widgets/tiles/statistics_tile.dart'; + +List _colorPalette = AppColor.values + .map((c) => getColorFromAppColor(c)) + .toList(); + +/// Returns the icon for the given statistic type. +IconData getStatisticIconForType(StatisticType type) => + _getStatisticIcon(type: type); + +/// Returns a color from the palette based on the statistic's ID. +Color getStatisticColorForStatistic(Statistic stat) => _getStatisticColor(stat); + +/// Computes the statistic values for a given [Statistic]. +List<(Player, num)> computeStatisticValues({ + required Statistic statistic, + required List matches, + required List players, +}) { + final filteredMatches = _getFilterMatches(statistic, matches); + final filteredPlayers = _getFilteredPlayers( + statistic, + players, + filteredMatches, + ); + + return _computeValuesForType( + type: statistic.type, + matches: filteredMatches, + players: filteredPlayers, + ); +} + +/// Build the [StatisticsTile] for a given [Statistic]. +Widget buildStatisticTile({ + required Statistic statistic, + required List matches, + required List players, + required BuildContext context, + double? width, +}) { + final values = computeStatisticValues( + statistic: statistic, + matches: matches, + players: players, + ); + + return StatisticsTile( + icon: _getStatisticIcon(type: statistic.type), + title: translateStatisticTypeToString(statistic.type, context), + width: width ?? MediaQuery.sizeOf(context).width * 0.95, + values: values, + barColor: _getStatisticColor(statistic), + displayCount: statistic.displayCount, + selectedGroups: statistic.selectedGroups, + selectedGames: statistic.selectedGames, + ); +} + +List _getFilterMatches(Statistic statistic, List matches) { + List filteredMatches = matches; + + // Filter timeframe + if (statistic.scopes.contains(StatisticScope.timeframe) && + statistic.timeframe != null) { + final minDate = _getMinimumDate(timeframe: statistic.timeframe!); + print( + 'Filtering matches by timeframe: ${statistic.timeframe}, minDate: $minDate', + ); + if (minDate != null) { + filteredMatches = matches + .where((m) => m.endedAt != null && m.endedAt!.isAfter(minDate)) + .toList(); + } + } + + // Filter games + if (statistic.scopes.contains(StatisticScope.selectedGames) && + (statistic.selectedGames?.isNotEmpty ?? false)) { + final gameIds = statistic.selectedGames!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((match) => gameIds.contains(match.game.id)) + .toList(); + } + + // Filter groups + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final groupIds = statistic.selectedGroups!.map((g) => g.id).toSet(); + filteredMatches = filteredMatches + .where((m) => m.group != null && groupIds.contains(m.group!.id)) + .toList(); + } + + return filteredMatches; +} + +/// Returns a [Player] List with the selected players depending on +List _getFilteredPlayers( + Statistic statistic, + List allPlayers, + List filteredMatches, +) { + // allPlayers + if (statistic.scopes.contains(StatisticScope.allPlayers)) { + return allPlayers; + } + + // selectedGroups -> only members + if (statistic.scopes.contains(StatisticScope.selectedGroups) && + (statistic.selectedGroups?.isNotEmpty ?? false)) { + final Set ids = { + for (final g in statistic.selectedGroups!) + for (final p in g.members) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); + } + + // Else -> all players from filtered matches + final Set ids = { + for (final m in filteredMatches) + for (final p in m.players) p.id, + }; + return allPlayers.where((p) => ids.contains(p.id)).toList(); +} + +/// Returns a [DateTime] with the minimum time and date the [timeframe] allows +DateTime? _getMinimumDate({required Timeframe timeframe}) { + final now = DateTime.now(); + switch (timeframe) { + case Timeframe.last7Days: + return now.subtract(const Duration(days: 7)); + case Timeframe.last30Days: + return now.subtract(const Duration(days: 30)); + case Timeframe.last90Days: + return now.subtract(const Duration(days: 90)); + case Timeframe.last180Days: + return now.subtract(const Duration(days: 180)); + case Timeframe.lastYear: + return now.subtract(const Duration(days: 365)); + case Timeframe.allTime: + return null; + } +} + +/// Computes the statistic values for each player based on the statistic type +/// and returns a list of (Player, value) tuples sorted descending by value. +List<(Player, num)> _computeValuesForType({ + required StatisticType type, + required List matches, + required List players, +}) { + switch (type) { + case StatisticType.totalMatches: + return _sortDesc( + players.map((p) => (p, _matchesPlayed(p, matches) as num)).toList(), + ); + + case StatisticType.totalWins: + return _sortDesc( + players.map((p) => (p, _wins(p, matches) as num)).toList(), + ); + + case StatisticType.totalLosses: + return _sortDesc( + players + .map( + (p) => + (p, (_matchesPlayed(p, matches) - _wins(p, matches)) as num), + ) + .toList(), + ); + + case StatisticType.totalScore: + return _sortDesc( + players.map((p) => (p, _totalScore(p, matches) as num)).toList(), + ); + + case StatisticType.averageScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final avg = scores.isEmpty + ? 0.0 + : double.parse( + (scores.reduce((a, b) => a + b) / scores.length) + .toStringAsFixed(2), + ); + return (p, avg as num); + }).toList(), + ); + + case StatisticType.bestScore: + return _sortDesc( + players.map((p) { + final scores = _scoresOf(p, matches); + final best = scores.isEmpty ? 0 : scores.reduce(max); + return (p, best as num); + }).toList(), + ); + + case StatisticType.worstScore: + // Ascending here is more meaningful for "worst", but keep the + // existing tile semantics (largest bar = top entry) by sorting + // descending on the inverse — i.e. show smallest score on top. + final entries = players.map((p) { + final scores = _scoresOf(p, matches); + final worst = scores.isEmpty ? 0 : scores.reduce(min); + return (p, worst as num); + }).toList(); + entries.sort((a, b) => a.$2.compareTo(b.$2)); + return entries; + + case StatisticType.winrate: + return _sortDesc( + players.map((p) { + final played = _matchesPlayed(p, matches); + final wins = _wins(p, matches); + final rate = played == 0 + ? 0.0 + : double.parse((wins / played).toStringAsFixed(2)); + return (p, rate as num); + }).toList(), + ); + } +} + +/* Helper functions for different statistic types */ + +/// Detemerines how many matches the player has played in the given list of matches. +int _matchesPlayed(Player p, List matches) => + matches.where((m) => m.players.any((mp) => mp.id == p.id)).length; + +/// Determines how many matches the player has won in the given list of matches. +int _wins(Player p, List matches) => + matches.where((m) => m.mvp.any((mp) => mp.id == p.id)).length; + +/// Determines the total score of the player in the given list of matches. +int _totalScore(Player p, List matches) { + var total = 0; + for (final m in matches) { + final s = m.scores[p.id]; + if (s != null) total += s.score; + } + return total; +} + +/// Returns a list of all scores the player has achieved in the given list of matches. +List _scoresOf(Player p, List matches) => [ + for (final m in matches) + if (m.scores[p.id] != null) m.scores[p.id]!.score, +]; + +/// Returns the list of entries sorted descending by the statistic value. +List<(Player, num)> _sortDesc(List<(Player, num)> entries) { + entries.sort((a, b) => b.$2.compareTo(a.$2)); + return entries; +} + +/* Icon and color */ + +/// Returns the icon for the given statistic type. +IconData _getStatisticIcon({required StatisticType type}) { + switch (type) { + case StatisticType.totalMatches: + return Icons.casino; + case StatisticType.totalWins: + return Icons.emoji_events; + case StatisticType.totalLosses: + return Icons.sentiment_dissatisfied; + case StatisticType.totalScore: + return Icons.scoreboard; + case StatisticType.averageScore: + return Icons.show_chart; + case StatisticType.bestScore: + return Icons.trending_up; + case StatisticType.worstScore: + return Icons.trending_down; + case StatisticType.winrate: + return Icons.percent; + } +} + +/// Returns a color from the palette based on the statistic's ID as random seed. +Color _getStatisticColor(Statistic stat) { + final seed = stat.id.hashCode; + return _colorPalette[seed.abs() % _colorPalette.length]; +} + +/* Skeleton data */ + +/// A placeholder tile with mock data for the loading state. +Widget buildSkeletonStatisticTile({required BuildContext context}) { + final count = 4 + Random().nextInt(5); // 4..8 + final values = <(Player, num)>[ + for (var i = 0; i < count; i++) + (Player(name: 'Player ${i + 1}'), count - i), + ]; + + return StatisticsTile( + icon: Icons.bar_chart, + title: 'Skeleton title', + width: MediaQuery.sizeOf(context).width * 0.95, + values: values, + barColor: _colorPalette[Random().nextInt(_colorPalette.length)], + selectedGames: [Game(name: 'Game 1', ruleset: Ruleset.highestScore)], + selectedGroups: [Group(name: 'Group 1', members: [])], + displayCount: 5, + ); +} diff --git a/lib/presentation/views/main_menu/statistics_view/statistics_view.dart b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart new file mode 100644 index 0000000..3381267 --- /dev/null +++ b/lib/presentation/views/main_menu/statistics_view/statistics_view.dart @@ -0,0 +1,190 @@ +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/data/db/database.dart'; +import 'package:tallee/data/models/match.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.dart'; +import 'package:tallee/l10n/generated/app_localizations.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/create_statistic_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_detail_view.dart'; +import 'package:tallee/presentation/views/main_menu/statistics_view/statistic_tile_factory.dart'; +import 'package:tallee/presentation/widgets/app_skeleton.dart'; +import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart'; +import 'package:tallee/presentation/widgets/top_centered_message.dart'; + +class StatisticsView extends StatefulWidget { + /// A view that displays player statistics + const StatisticsView({super.key}); + + @override + State createState() => _StatisticsViewState(); +} + +class _StatisticsViewState extends State { + bool isLoading = true; + List _allMatches = const []; + List _allPlayers = const []; + List _statistics = const []; + List statisticTiles = []; + + @override + void initState() { + super.initState(); + + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + loadStatistics(context); + }); + } + + @override + Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + return Stack( + alignment: AlignmentDirectional.bottomCenter, + fit: StackFit.expand, + children: [ + Visibility( + visible: statisticTiles.isNotEmpty, + replacement: Center( + child: TopCenteredMessage( + icon: Icons.info, + title: loc.info, + message: loc.no_statistics_created_yet, + ), + ), + child: SingleChildScrollView( + child: AppSkeleton( + enabled: isLoading, + fixLayoutBuilder: true, + child: Column( + spacing: 12, + mainAxisAlignment: MainAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + ...statisticTiles, + SizedBox( + height: MediaQuery.paddingOf(context).bottom + 80, + ), + ], + ), + ), + ), + ), + Positioned( + bottom: MediaQuery.paddingOf(context).bottom + 20, + child: MainMenuButton( + text: loc.create_statistic, + icon: Icons.bar_chart, + onPressed: () async { + Statistic newStatistic = await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => CreateStatisticView( + onStatisticCreated: () => loadStatistics(context), + ), + ), + ); + if (!context.mounted) return; + setState(() { + _statistics = [..._statistics, newStatistic]; + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + }); + }, + ), + ), + ], + ); + }, + ); + } + + Future loadStatistics(BuildContext context) async { + setState(() { + isLoading = true; + statisticTiles = List.generate( + 4, + (index) => Column( + children: [ + buildSkeletonStatisticTile(context: context), + const SizedBox(height: 12), + ], + ), + ); + }); + + final db = Provider.of(context, listen: false); + + final results = await Future.wait([ + db.statisticDao.getAllStatistics(), + db.matchDao.getAllMatches(), + db.playerDao.getAllPlayers(), + Future.delayed(Constants.MINIMUM_SKELETON_DURATION), + ]); + + if (!mounted) return; + + final statistics = results[0] as List; + _allMatches = results[1] as List; + _allPlayers = results[2] as List; + _statistics = statistics; + + setState(() { + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + isLoading = false; + }); + } + + Widget _buildStatisticTile(BuildContext context, Statistic statistic) { + final values = computeStatisticValues( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + ); + + return GestureDetector( + onTap: () async { + final newDisplayCount = await Navigator.push( + context, + adaptivePageRoute( + builder: (context) => StatisticDetailView( + statistic: statistic, + values: values, + icon: getStatisticIconForType(statistic.type), + barColor: getStatisticColorForStatistic(statistic), + ), + ), + ); + if (newDisplayCount != null && + newDisplayCount != statistic.displayCount) { + setState(() { + _statistics = _statistics + .map( + (stat) => stat.id == statistic.id + ? stat.copyWith(displayCount: newDisplayCount) + : stat, + ) + .toList(); + statisticTiles = _statistics + .map((stat) => _buildStatisticTile(context, stat)) + .toList(); + }); + } + }, + child: buildStatisticTile( + statistic: statistic, + matches: _allMatches, + players: _allPlayers, + context: context, + ), + ); + } +} diff --git a/lib/presentation/widgets/game_label.dart b/lib/presentation/widgets/game_label.dart index 553e637..2e6bf74 100644 --- a/lib/presentation/widgets/game_label.dart +++ b/lib/presentation/widgets/game_label.dart @@ -12,41 +12,44 @@ class GameLabel extends StatelessWidget { final String title; final String description; - final GameColor color; + final AppColor color; @override Widget build(BuildContext context) { - final backgroundColor = getColorFromGameColor(color); + final backgroundColor = getColorFromAppColor(color); final fontColor = backgroundColor.computeLuminance() > 0.5 ? Colors.black : Colors.white; - return IntrinsicHeight( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - // Title - Container( - decoration: BoxDecoration( - color: backgroundColor.withAlpha(230), - borderRadius: const BorderRadius.only( - topLeft: Radius.circular(8), - bottomLeft: Radius.circular(8), - ), - ), - padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), - child: Text( - title, - style: TextStyle( - fontSize: 12, - color: fontColor, - fontWeight: FontWeight.bold, - ), + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + // Title + Container( + decoration: BoxDecoration( + color: backgroundColor.withAlpha(230), + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(8), + bottomLeft: Radius.circular(8), ), ), + padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), + child: Text( + title, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, + style: TextStyle( + fontSize: 12, + color: fontColor, + fontWeight: FontWeight.bold, + ), + ), + ), - // Description - Container( + // Description + Flexible( + child: Container( decoration: BoxDecoration( color: backgroundColor.withAlpha(140), borderRadius: const BorderRadius.only( @@ -57,6 +60,9 @@ class GameLabel extends StatelessWidget { padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8), child: Text( description, + maxLines: 1, + overflow: TextOverflow.ellipsis, + softWrap: false, style: TextStyle( fontSize: 12, color: fontColor, @@ -64,8 +70,8 @@ class GameLabel extends StatelessWidget { ), ), ), - ], - ), + ), + ], ); } } diff --git a/lib/presentation/widgets/tiles/game_tile.dart b/lib/presentation/widgets/tiles/game_tile.dart index ee5acf0..4fb12d1 100644 --- a/lib/presentation/widgets/tiles/game_tile.dart +++ b/lib/presentation/widgets/tiles/game_tile.dart @@ -51,7 +51,7 @@ class GameTile extends StatelessWidget { ? (badgeColor!.computeLuminance() > 0.5 ? Colors.black : Colors.white) : Colors.white; - final gameColor = badgeColor ?? getColorFromGameColor(GameColor.orange); + final gameColor = badgeColor ?? getColorFromAppColor(AppColor.orange); return GestureDetector( onTap: () async { diff --git a/lib/presentation/widgets/tiles/statistics_tile.dart b/lib/presentation/widgets/tiles/statistics_tile.dart index ea9cb49..02cdb75 100644 --- a/lib/presentation/widgets/tiles/statistics_tile.dart +++ b/lib/presentation/widgets/tiles/statistics_tile.dart @@ -1,8 +1,12 @@ import 'dart:math'; import 'package:flutter/material.dart'; +import 'package:fluttericon/rpg_awesome_icons.dart'; import 'package:tallee/core/common.dart'; import 'package:tallee/core/custom_theme.dart'; +import 'package:tallee/core/enums.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/player.dart'; import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; @@ -21,8 +25,11 @@ class StatisticsTile extends StatelessWidget { required this.title, required this.width, required this.values, - required this.itemCount, required this.barColor, + required this.displayCount, + this.selectedGroups, + this.selectedGames, + this.showAllValues = false, }); /// The icon displayed next to the title. @@ -37,12 +44,16 @@ class StatisticsTile extends StatelessWidget { /// A list of tuples containing labels and their corresponding numeric values. final List<(Player, num)> values; - /// The maximum number of items to display. - final int itemCount; - /// The color of the bars representing the values. final Color barColor; + // statistic data + final int displayCount; + final List? selectedGroups; + final List? selectedGames; + + final bool showAllValues; + @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); @@ -52,91 +63,202 @@ class StatisticsTile extends StatelessWidget { title: title, icon: icon, content: Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), + padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Visibility( visible: values.isNotEmpty, + + // No data avaiable message replacement: Center( heightFactor: 4, child: Text(loc.no_data_available), ), + + // Bar chart child: LayoutBuilder( builder: (context, constraints) { - final maxBarWidth = constraints.maxWidth * 0.65; + final maxBarWidth = constraints.maxWidth * 0.8; + + // If displayCount wasnt provided, take all values + final valuesShown = showAllValues + ? values.length + : min(values.length, displayCount); + final displayValues = values.take(valuesShown).toList(); + final maxVal = displayValues.isNotEmpty + ? displayValues.fold( + 0, + (currentMax, entry) => + entry.$2 > currentMax ? entry.$2 : currentMax, + ) + : 0; + return Column( - children: List.generate(min(values.length, itemCount), (index) { - /// The maximum wins among all players - final maxMatches = values.isNotEmpty ? values[0].$2 : 0; + children: [ + // Bars + ...List.generate(valuesShown, (index) { + /// Fraction of wins + final double fraction = (maxVal > 0) + ? (displayValues[index].$2 / maxVal) + : 0.0; - /// Fraction of wins - final double fraction = (maxMatches > 0) - ? (values[index].$2 / maxMatches) - : 0.0; + /// Calculated width for current the bar + final double barWidth = (maxBarWidth * fraction).clamp( + 0.0, + maxBarWidth, + ); - /// Calculated width for current the bar - final double barWidth = maxBarWidth * fraction; + final barClr = index >= displayCount + ? barColor.withAlpha(150) + : barColor; - return Padding( - padding: const EdgeInsets.symmetric(vertical: 2.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - Stack( - children: [ - Container( - height: 24, - width: barWidth, - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4), - color: barColor, - ), - ), - Padding( - padding: const EdgeInsets.only(left: 4.0), - child: RichText( - overflow: TextOverflow.ellipsis, - text: TextSpan( - style: DefaultTextStyle.of(context).style, - children: [ - TextSpan( - text: values[index].$1.name, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: getNameCountText(values[index].$1), - style: TextStyle( - fontSize: 14, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor.withAlpha( - 150, - ), - ), - ), - ], + var textClr = barColor.computeLuminance() > 0.5 + ? const Color(0xFF101010) + : CustomTheme.textColor; + textClr = textClr.withAlpha( + index >= displayCount ? 220 : 255, + ); + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 2.0), + child: Row( + mainAxisAlignment: MainAxisAlignment.start, + children: [ + SizedBox( + width: maxBarWidth, + child: Stack( + clipBehavior: Clip.hardEdge, + children: [ + // Bar + Container( + height: 24, + width: barWidth, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(4), + color: barClr, + ), ), - ), - ), - ], - ), - const Spacer(), - Center( - child: Text( - values[index].$2 <= 1 && values[index].$2 is double - ? values[index].$2.toStringAsFixed(2) - : values[index].$2.toString(), - textAlign: TextAlign.center, - style: const TextStyle( - fontSize: 16, - fontWeight: FontWeight.bold, + + // Player + Padding( + padding: const EdgeInsets.only(left: 4.0), + child: RichText( + maxLines: 1, + softWrap: false, + overflow: TextOverflow.ellipsis, + text: TextSpan( + style: DefaultTextStyle.of(context).style, + children: [ + TextSpan( + text: displayValues[index].$1.name, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: textClr, + ), + ), + TextSpan( + text: getNameCountText( + displayValues[index].$1, + ), + style: TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: + (barColor == + getColorFromAppColor( + AppColor.yellow, + ) + ? const Color( + 0xFF101010, + ) + : CustomTheme.textColor) + .withAlpha(150), + ), + ), + ], + ), + ), + ), + ], ), ), - ), - ], + const Spacer(), + + // Value + Center( + child: Text( + displayValues[index].$2 <= 1 && + displayValues[index].$2 is double + ? displayValues[index].$2.toStringAsFixed(2) + : displayValues[index].$2.toString(), + textAlign: TextAlign.center, + style: const TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + ), + ), + ), + ], + ), + ); + }), + + // Group & Game info + if (hasGame || hasGroup) + Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Wrap( + alignment: WrapAlignment.start, + crossAxisAlignment: WrapCrossAlignment.center, + spacing: 4, + runSpacing: 4, + children: [ + // Game + if (hasGroup) + Row( + spacing: 8, + children: [ + const Icon( + RpgAwesome.clovers_card, + color: CustomTheme.hintColor, + size: 20, + ), + Text( + getGameText(selectedGames!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + if (hasGroup && hasGame) const SizedBox(width: 20), + + // Group + if (hasGroup) + Row( + spacing: 8, + children: [ + const Icon( + Icons.groups, + color: CustomTheme.hintColor, + ), + Text( + getGroupText(selectedGroups!), + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.bold, + color: CustomTheme.hintColor, + ), + overflow: TextOverflow.ellipsis, + ), + ], + ), + ], + ), ), - ); - }), + ], ); }, ), @@ -144,4 +266,24 @@ class StatisticsTile extends StatelessWidget { ), ); } + + String getGroupText(List groups) { + var text = groups[0].name; + if (groups.length > 1) { + return '$text + ${groups.length - 1}'; + } + return text; + } + + String getGameText(List games) { + var text = games[0].name; + if (games.length > 1) { + return '$text + ${games.length - 1}'; + } + return text; + } + + bool get hasGroup => selectedGroups != null && selectedGroups!.isNotEmpty; + + bool get hasGame => selectedGames != null && selectedGames!.isNotEmpty; } diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 29199f8..9dbf955 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -20,6 +20,7 @@ class DataTransferService { static Future deleteAllData(BuildContext context) async { final db = Provider.of(context, listen: false); + await db.statisticDao.deleteAllStatistics(); await db.matchDao.deleteAllMatches(); await db.teamDao.deleteAllTeams(); await db.groupDao.deleteAllGroups(); @@ -278,7 +279,7 @@ class DataTransferService { name: 'Unknown', ruleset: Ruleset.singleWinner, description: '', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); } diff --git a/pubspec.lock b/pubspec.lock index 96b2cc5..fc30e57 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -17,6 +17,14 @@ packages: url: "https://pub.dev" source: hosted version: "10.0.1" + animated_custom_dropdown: + dependency: "direct main" + description: + name: animated_custom_dropdown + sha256: "5a72dc209041bb53f6c7164bc2e366552d5197cdb032b1c9b2c36e3013024486" + url: "https://pub.dev" + source: hosted + version: "3.1.1" arb_utils: dependency: "direct dev" description: @@ -353,6 +361,14 @@ packages: url: "https://pub.dev" source: hosted version: "0.2.8" + dropdown_flutter: + dependency: "direct main" + description: + name: dropdown_flutter + sha256: "5ae3d05d768d0bb6030ff735e6b4b93f7b29be3cf3bec7c86cd4f444c8f067ff" + url: "https://pub.dev" + source: hosted + version: "1.0.3" equatable: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 3d8d99f..183335c 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,19 +1,21 @@ name: tallee description: "Tracking App for Card Games" publish_to: 'none' -version: 0.0.33+273 +version: 0.0.33+281 environment: - sdk: ^3.8.1 + sdk: ^3.12.0 dependencies: + animated_custom_dropdown: ^3.1.1 clock: ^1.1.2 collection: ^1.19.1 - cupertino_icons: ^1.0.6 - drift: ^2.27.0 - drift_flutter: ^0.2.4 + dropdown_flutter: ^1.0.3 + cupertino_icons: ^1.0.9 + drift: ^2.33.0 + drift_flutter: ^0.3.0 file_picker: ^11.0.2 - file_saver: ^0.3.1 + file_saver: ^0.4.0 flutter: sdk: flutter flutter_localizations: @@ -24,20 +26,20 @@ dependencies: font_awesome_flutter: ^11.0.0 intl: any json_schema: ^5.2.2 - package_info_plus: ^9.0.0 + package_info_plus: ^9.0.1 path_provider: ^2.1.5 provider: ^6.1.5 - skeletonizer: ^2.1.0+1 + skeletonizer: ^2.1.3 url_launcher: ^6.3.2 - uuid: ^4.5.2 + uuid: ^4.5.3 dev_dependencies: arb_utils: ^0.11.0 flutter_test: sdk: flutter - build_runner: ^2.7.0 - dart_pubspec_licenses: ^3.0.14 - drift_dev: ^2.27.0 + build_runner: ^2.15.0 + dart_pubspec_licenses: ^3.2.0 + drift_dev: ^2.33.0 flutter_lints: ^6.0.0 flutter: diff --git a/test/db_tests/aggregates/match_test.dart b/test/db_tests/aggregates/match_test.dart index 00e6e46..1144a73 100644 --- a/test/db_tests/aggregates/match_test.dart +++ b/test/db_tests/aggregates/match_test.dart @@ -56,7 +56,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/aggregates/team_test.dart b/test/db_tests/aggregates/team_test.dart index fefdcc5..381d22b 100644 --- a/test/db_tests/aggregates/team_test.dart +++ b/test/db_tests/aggregates/team_test.dart @@ -49,7 +49,7 @@ void main() { testGame = Game( name: 'Test Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/entities/game_test.dart b/test/db_tests/entities/game_test.dart index 778d43b..f7e7dcd 100644 --- a/test/db_tests/entities/game_test.dart +++ b/test/db_tests/entities/game_test.dart @@ -28,7 +28,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'A classic strategy game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); testGame2 = Game( @@ -36,7 +36,7 @@ void main() { name: 'Poker', ruleset: Ruleset.multipleWinners, description: 'Card game with multiple winners', - color: GameColor.red, + color: AppColor.red, icon: 'poker_icon', ); testGame3 = Game( @@ -44,7 +44,7 @@ void main() { name: 'Monopoly', ruleset: Ruleset.highestScore, description: 'A board game about real estate', - color: GameColor.orange, + color: AppColor.orange, icon: '', ); }); @@ -124,7 +124,7 @@ void main() { name: 'Game\'s & "Special" ', ruleset: Ruleset.multipleWinners, description: 'Description with émojis 🎮🎲', - color: GameColor.purple, + color: AppColor.purple, icon: '', ); await database.gameDao.addGame(game: specialGame); @@ -280,19 +280,19 @@ void main() { await database.gameDao.updateGameColor( gameId: testGame1.id, - color: GameColor.green, + color: AppColor.green, ); final updatedGame = await database.gameDao.getGameById( gameId: testGame1.id, ); - expect(updatedGame.color, GameColor.green); + expect(updatedGame.color, AppColor.green); }); test('updateGameColor() does nothing for non-existent game', () async { final updated = await database.gameDao.updateGameColor( gameId: 'non-existent-id', - color: GameColor.green, + color: AppColor.green, ); expect(updated, isFalse); @@ -336,7 +336,7 @@ void main() { name: newName, ); - const newGameColor = GameColor.teal; + const newGameColor = AppColor.teal; await database.gameDao.updateGameColor( gameId: testGame1.id, color: newGameColor, diff --git a/test/db_tests/relationships/player_match_test.dart b/test/db_tests/relationships/player_match_test.dart index 6d879c3..fa7ec21 100644 --- a/test/db_tests/relationships/player_match_test.dart +++ b/test/db_tests/relationships/player_match_test.dart @@ -42,7 +42,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/db_tests/statistics/statistic_test.dart b/test/db_tests/statistics/statistic_test.dart new file mode 100644 index 0000000..bd590c7 --- /dev/null +++ b/test/db_tests/statistics/statistic_test.dart @@ -0,0 +1,122 @@ +import 'dart:core'; + +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/core/enums.dart'; +import 'package:tallee/data/db/database.dart'; +import 'package:tallee/data/models/game.dart'; +import 'package:tallee/data/models/group.dart'; +import 'package:tallee/data/models/player.dart'; +import 'package:tallee/data/models/statistic.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 Game testGame; + /*late Match testMatch1; + late Match testMatch2; + late Match testMatchOnlyPlayers; + late Match testMatchOnlyGroup;*/ + 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 1', + 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: AppColor.blue, + icon: '', + ); + /*testMatch1 = Match( + name: 'First Test Match', + game: testGame, + group: testGroup1, + players: [testPlayer4, testPlayer5], + scores: {testPlayer4.id: ScoreEntry(score: 1)}, + ); + testMatch2 = Match( + name: 'Second Test Match', + game: testGame, + group: testGroup2, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyPlayers = Match( + name: 'Test Match with Players', + game: testGame, + players: [testPlayer1, testPlayer2, testPlayer3], + ); + testMatchOnlyGroup = Match( + name: 'Test Match with Group', + game: testGame, + group: testGroup2, + players: testGroup2.members, + );*/ + }); + await database.playerDao.addPlayersAsList( + players: [ + testPlayer1, + testPlayer2, + testPlayer3, + testPlayer4, + testPlayer5, + ], + ); + await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]); + await database.gameDao.addGame(game: testGame); + }); + tearDown(() async { + await database.close(); + }); + + test('Adding/fetching statistic works correclty', () async { + final statistic = Statistic( + type: StatisticType.averageScore, + scopes: [StatisticScope.allPlayers, StatisticScope.selectedGames], + timeframe: Timeframe.allTime, + selectedGames: [testGame], + selectedGroups: [testGroup1], + ); + + final added = await database.statisticDao.addStatistic( + statistic: statistic, + ); + expect(added, isTrue); + + final fetched = await database.statisticDao.getStatisticById(statistic.id); + expect(fetched, isNotNull); + expect(fetched!.type, statistic.type); + }); +} diff --git a/test/db_tests/values/score_entry_test.dart b/test/db_tests/values/score_entry_test.dart index f6cc292..593d194 100644 --- a/test/db_tests/values/score_entry_test.dart +++ b/test/db_tests/values/score_entry_test.dart @@ -40,7 +40,7 @@ void main() { name: 'Test Game', ruleset: Ruleset.singleWinner, description: 'A test game', - color: GameColor.blue, + color: AppColor.blue, icon: '', ); testMatch1 = Match( diff --git a/test/services/data_transfer_service_test.dart b/test/services/data_transfer_service_test.dart index 586138a..94ed977 100644 --- a/test/services/data_transfer_service_test.dart +++ b/test/services/data_transfer_service_test.dart @@ -45,7 +45,7 @@ void main() { name: 'Chess', ruleset: Ruleset.singleWinner, description: 'Strategic board game', - color: GameColor.blue, + color: AppColor.blue, icon: 'chess_icon', ); @@ -448,19 +448,19 @@ void main() { Game( name: 'Red Game', ruleset: Ruleset.singleWinner, - color: GameColor.red, + color: AppColor.red, icon: 'icon', ), Game( name: 'Blue Game', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Green Game', ruleset: Ruleset.singleWinner, - color: GameColor.green, + color: AppColor.green, icon: 'icon', ), ]; @@ -484,19 +484,19 @@ void main() { Game( name: 'Highest Score Game', ruleset: Ruleset.highestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Lowest Score Game', ruleset: Ruleset.lowestScore, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), Game( name: 'Single Winner', ruleset: Ruleset.singleWinner, - color: GameColor.blue, + color: AppColor.blue, icon: 'icon', ), ];