23 Commits

Author SHA1 Message Date
ddf32797aa Implemented ruleset in match view
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 43s
Pull Request Pipeline / lint (pull_request) Failing after 46s
2026-04-14 23:26:26 +02:00
df1bc4bf32 Merge branch 'development' into feature/132-verschiedene-regelsaetze-implementieren
# Conflicts:
#	lib/presentation/views/main_menu/match_view/match_result_view.dart
2026-04-14 23:09:15 +02:00
36fda30f27 Updated licenses [skip ci] 2026-04-14 16:13:31 +00:00
1ab869ec26 Updated version number [skip ci] 2026-04-14 16:12:55 +00:00
52b78e44e4 Merge pull request 'Score implementation ergänzen' (#196) from feature/191-score-implementation-ergaenzen into development
All checks were successful
Push Pipeline / test (push) Successful in 42s
Push Pipeline / update_version (push) Successful in 5s
Push Pipeline / generate_licenses (push) Successful in 35s
Push Pipeline / format (push) Successful in 54s
Push Pipeline / build (push) Successful in 5m32s
Reviewed-on: #196
Reviewed-by: gelbeinhalb <spam@yannick-weigert.de>
2026-04-14 16:12:05 +00:00
e827f4c527 Fixed references
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 42s
Pull Request Pipeline / lint (pull_request) Successful in 47s
2026-04-13 22:53:39 +02:00
73c85b1ff2 Renamed score dao
Some checks failed
Pull Request Pipeline / lint (pull_request) Failing after 44s
Pull Request Pipeline / test (pull_request) Failing after 1m33s
2026-04-13 22:49:30 +02:00
c43b7b478c Updated comments
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 44s
Pull Request Pipeline / lint (pull_request) Successful in 46s
2026-04-13 22:47:29 +02:00
c9188c222a Added edge cases for games, groups, teams and matches 2026-04-13 22:16:34 +02:00
fcca74cea5 Refactoring 2026-04-13 22:16:12 +02:00
80672343b9 Refactoring
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 41s
Pull Request Pipeline / lint (pull_request) Successful in 48s
2026-04-12 02:02:27 +02:00
d903a9fd7e Refactoring 2026-04-12 02:02:16 +02:00
bed8a05057 Refactoring 2026-04-12 02:01:26 +02:00
8a312152a5 Refactoring 2026-04-12 02:01:12 +02:00
eeb68496d5 Removed tester input
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 41s
Pull Request Pipeline / lint (pull_request) Successful in 45s
2026-04-12 01:42:47 +02:00
9b2fcf1860 Fix: Linter exclude
All checks were successful
Pull Request Pipeline / test (pull_request) Successful in 40s
Pull Request Pipeline / lint (pull_request) Successful in 46s
2026-03-08 22:27:27 +01:00
f0c575d2c9 Implemented different result view depending on ruleset
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 38s
Pull Request Pipeline / lint (pull_request) Failing after 45s
2026-03-08 22:25:55 +01:00
d5a7bb320f Implememented different result tiles in match detail view for different rulesets 2026-03-08 22:25:23 +01:00
84b8541822 Fixed match edit error with game
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 39s
Pull Request Pipeline / lint (pull_request) Failing after 46s
2026-03-08 11:00:42 +01:00
544e55c4fd Merge branch 'development' into feature/132-verschiedene-regelsaetze-implementieren
# Conflicts:
#	lib/l10n/arb/app_de.arb
#	lib/l10n/arb/app_en.arb
#	lib/l10n/generated/app_localizations.dart
#	lib/l10n/generated/app_localizations_de.dart
#	lib/l10n/generated/app_localizations_en.dart
#	lib/presentation/views/main_menu/match_view/match_result_view.dart
2026-03-08 10:57:33 +01:00
c50ad288fa Implemented basic structure
Some checks failed
Pull Request Pipeline / test (pull_request) Successful in 39s
Pull Request Pipeline / lint (pull_request) Failing after 44s
2026-03-08 08:29:45 +01:00
494dec8c61 Added localizations 2026-03-08 08:29:15 +01:00
975679b048 Updated dependencie 2026-03-08 08:24:35 +01:00
30 changed files with 1143 additions and 658 deletions

View File

@@ -1,5 +1,9 @@
include: package:flutter_lints/flutter.yaml include: package:flutter_lints/flutter.yaml
analyzer:
exclude:
- lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart
linter: linter:
rules: rules:
avoid_print: false avoid_print: false
@@ -11,8 +15,4 @@ linter:
prefer_const_literals_to_create_immutables: true prefer_const_literals_to_create_immutables: true
unnecessary_const: true unnecessary_const: true
lines_longer_than_80_chars: false lines_longer_than_80_chars: false
constant_identifier_names: false constant_identifier_names: false
analyzer:
exclude:
- lib/presentation/views/main_menu/settings_view/licenses/oss_licenses.dart

View File

@@ -85,21 +85,21 @@ class CustomTheme {
); );
static const SearchBarThemeData searchBarTheme = SearchBarThemeData( static const SearchBarThemeData searchBarTheme = SearchBarThemeData(
textStyle: WidgetStatePropertyAll(TextStyle(color: CustomTheme.textColor)), textStyle: WidgetStatePropertyAll(TextStyle(color: textColor)),
hintStyle: WidgetStatePropertyAll(TextStyle(color: CustomTheme.hintColor)), hintStyle: WidgetStatePropertyAll(TextStyle(color: hintColor)),
); );
static final RadioThemeData radioTheme = RadioThemeData( static final RadioThemeData radioTheme = RadioThemeData(
fillColor: WidgetStateProperty.resolveWith<Color>((states) { fillColor: WidgetStateProperty.resolveWith<Color>((states) {
if (states.contains(WidgetState.selected)) { if (states.contains(WidgetState.selected)) {
return CustomTheme.primaryColor; return primaryColor;
} }
return CustomTheme.textColor; return textColor;
}), }),
); );
static const InputDecorationTheme inputDecorationTheme = InputDecorationTheme( static const InputDecorationTheme inputDecorationTheme = InputDecorationTheme(
labelStyle: TextStyle(color: CustomTheme.textColor), labelStyle: TextStyle(color: textColor),
hintStyle: TextStyle(color: CustomTheme.hintColor), hintStyle: TextStyle(color: hintColor),
); );
} }

View File

@@ -30,9 +30,11 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
final players = final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? []; await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? [];
final scores = await db.scoreDao.getAllMatchScores(matchId: row.id); final scores = await db.scoreEntryDao.getAllMatchScores(
matchId: row.id,
);
final winner = await db.scoreDao.getWinner(matchId: row.id); final winner = await db.scoreEntryDao.getWinner(matchId: row.id);
return Match( return Match(
id: row.id, id: row.id,
name: row.name, name: row.name,
@@ -64,9 +66,9 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
final players = final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? []; await db.playerMatchDao.getPlayersOfMatch(matchId: matchId) ?? [];
final scores = await db.scoreDao.getAllMatchScores(matchId: matchId); final scores = await db.scoreEntryDao.getAllMatchScores(matchId: matchId);
final winner = await db.scoreDao.getWinner(matchId: matchId); final winner = await db.scoreEntryDao.getWinner(matchId: matchId);
return Match( return Match(
id: result.id, id: result.id,
@@ -109,15 +111,15 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
for (final pid in match.scores.keys) { for (final pid in match.scores.keys) {
final playerScores = match.scores[pid]!; final playerScores = match.scores[pid]!;
await db.scoreDao.addScoresAsList( await db.scoreEntryDao.addScoresAsList(
scores: playerScores, entrys: playerScores,
playerId: pid, playerId: pid,
matchId: match.id, matchId: match.id,
); );
} }
if (match.winner != null) { if (match.winner != null) {
await db.scoreDao.setWinner( await db.scoreEntryDao.setWinner(
matchId: match.id, matchId: match.id,
playerId: match.winner!.id, playerId: match.winner!.id,
); );
@@ -298,7 +300,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
final group = await db.groupDao.getGroupById(groupId: groupId); final group = await db.groupDao.getGroupById(groupId: groupId);
final players = final players =
await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? []; await db.playerMatchDao.getPlayersOfMatch(matchId: row.id) ?? [];
final winner = await db.scoreDao.getWinner(matchId: row.id); final winner = await db.scoreEntryDao.getWinner(matchId: row.id);
return Match( return Match(
id: row.id, id: row.id,
name: row.name, name: row.name,

View File

@@ -2,45 +2,44 @@ import 'dart:async';
import 'package:drift/drift.dart'; import 'package:drift/drift.dart';
import 'package:tallee/data/db/database.dart'; import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/db/tables/score_table.dart'; import 'package:tallee/data/db/tables/score_entry_table.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score.dart'; import 'package:tallee/data/models/score_entry.dart';
part 'score_dao.g.dart'; part 'score_entry_dao.g.dart';
@DriftAccessor(tables: [ScoreTable]) @DriftAccessor(tables: [ScoreEntryTable])
class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin { class ScoreEntryDao extends DatabaseAccessor<AppDatabase>
ScoreDao(super.db); with _$ScoreEntryDaoMixin {
ScoreEntryDao(super.db);
/// Adds a score entry to the database. /// Adds a score entry to the database.
Future<void> addScore({ Future<void> addScore({
required String playerId, required String playerId,
required String matchId, required String matchId,
required int score, required ScoreEntry entry,
int change = 0,
int roundNumber = 0,
}) async { }) async {
await into(scoreTable).insert( await into(scoreEntryTable).insert(
ScoreTableCompanion.insert( ScoreEntryTableCompanion.insert(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: roundNumber, roundNumber: entry.roundNumber,
score: score, score: entry.score,
change: change, change: entry.change,
), ),
mode: InsertMode.insertOrReplace, mode: InsertMode.insertOrReplace,
); );
} }
Future<void> addScoresAsList({ Future<void> addScoresAsList({
required List<Score> scores, required List<ScoreEntry> entrys,
required String playerId, required String playerId,
required String matchId, required String matchId,
}) async { }) async {
if (scores.isEmpty) return; if (entrys.isEmpty) return;
final entries = scores final entries = entrys
.map( .map(
(score) => ScoreTableCompanion.insert( (score) => ScoreEntryTableCompanion.insert(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: score.roundNumber, roundNumber: score.roundNumber,
@@ -51,17 +50,21 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
.toList(); .toList();
await batch((batch) { await batch((batch) {
batch.insertAll(scoreTable, entries, mode: InsertMode.insertOrReplace); batch.insertAll(
scoreEntryTable,
entries,
mode: InsertMode.insertOrReplace,
);
}); });
} }
/// Retrieves the score for a specific round. /// Retrieves the score for a specific round.
Future<Score?> getScore({ Future<ScoreEntry?> getScore({
required String playerId, required String playerId,
required String matchId, required String matchId,
int roundNumber = 0, int roundNumber = 0,
}) async { }) async {
final query = select(scoreTable) final query = select(scoreEntryTable)
..where( ..where(
(s) => (s) =>
s.playerId.equals(playerId) & s.playerId.equals(playerId) &
@@ -72,7 +75,7 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
final result = await query.getSingleOrNull(); final result = await query.getSingleOrNull();
if (result == null) return null; if (result == null) return null;
return Score( return ScoreEntry(
roundNumber: result.roundNumber, roundNumber: result.roundNumber,
score: result.score, score: result.score,
change: result.change, change: result.change,
@@ -80,15 +83,16 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
} }
/// Retrieves all scores for a specific match. /// Retrieves all scores for a specific match.
Future<Map<String, List<Score>>> getAllMatchScores({ Future<Map<String, List<ScoreEntry>>> getAllMatchScores({
required String matchId, required String matchId,
}) async { }) async {
final query = select(scoreTable)..where((s) => s.matchId.equals(matchId)); final query = select(scoreEntryTable)
..where((s) => s.matchId.equals(matchId));
final result = await query.get(); final result = await query.get();
final Map<String, List<Score>> scoresByPlayer = {}; final Map<String, List<ScoreEntry>> scoresByPlayer = {};
for (final row in result) { for (final row in result) {
final score = Score( final score = ScoreEntry(
roundNumber: row.roundNumber, roundNumber: row.roundNumber,
score: row.score, score: row.score,
change: row.change, change: row.change,
@@ -100,17 +104,17 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
} }
/// Retrieves all scores for a specific player in a match. /// Retrieves all scores for a specific player in a match.
Future<List<Score>> getAllPlayerScoresInMatch({ Future<List<ScoreEntry>> getAllPlayerScoresInMatch({
required String playerId, required String playerId,
required String matchId, required String matchId,
}) async { }) async {
final query = select(scoreTable) final query = select(scoreEntryTable)
..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)) ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId))
..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]); ..orderBy([(s) => OrderingTerm.asc(s.roundNumber)]);
final result = await query.get(); final result = await query.get();
return result return result
.map( .map(
(row) => Score( (row) => ScoreEntry(
roundNumber: row.roundNumber, roundNumber: row.roundNumber,
score: row.score, score: row.score,
change: row.change, change: row.change,
@@ -126,21 +130,19 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
Future<bool> updateScore({ Future<bool> updateScore({
required String playerId, required String playerId,
required String matchId, required String matchId,
required int newScore, required ScoreEntry newEntry,
int newChange = 0,
int roundNumber = 0,
}) async { }) async {
final rowsAffected = final rowsAffected =
await (update(scoreTable)..where( await (update(scoreEntryTable)..where(
(s) => (s) =>
s.playerId.equals(playerId) & s.playerId.equals(playerId) &
s.matchId.equals(matchId) & s.matchId.equals(matchId) &
s.roundNumber.equals(roundNumber), s.roundNumber.equals(newEntry.roundNumber),
)) ))
.write( .write(
ScoreTableCompanion( ScoreEntryTableCompanion(
score: Value(newScore), score: Value(newEntry.score),
change: Value(newChange), change: Value(newEntry.change),
), ),
); );
return rowsAffected > 0; return rowsAffected > 0;
@@ -152,7 +154,7 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
required String matchId, required String matchId,
int roundNumber = 0, int roundNumber = 0,
}) async { }) async {
final query = delete(scoreTable) final query = delete(scoreEntryTable)
..where( ..where(
(s) => (s) =>
s.playerId.equals(playerId) & s.playerId.equals(playerId) &
@@ -164,7 +166,8 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
} }
Future<bool> deleteAllScoresForMatch({required String matchId}) async { Future<bool> deleteAllScoresForMatch({required String matchId}) async {
final query = delete(scoreTable)..where((s) => s.matchId.equals(matchId)); final query = delete(scoreEntryTable)
..where((s) => s.matchId.equals(matchId));
final rowsAffected = await query.go(); final rowsAffected = await query.go();
return rowsAffected > 0; return rowsAffected > 0;
} }
@@ -173,7 +176,7 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
required String matchId, required String matchId,
required String playerId, required String playerId,
}) async { }) async {
final query = delete(scoreTable) final query = delete(scoreEntryTable)
..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId)); ..where((s) => s.playerId.equals(playerId) & s.matchId.equals(matchId));
final rowsAffected = await query.go(); final rowsAffected = await query.go();
return rowsAffected > 0; return rowsAffected > 0;
@@ -182,11 +185,11 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
/// Gets the highest (latest) round number for a match. /// Gets the highest (latest) round number for a match.
/// Returns `null` if there are no scores for the match. /// Returns `null` if there are no scores for the match.
Future<int?> getLatestRoundNumber({required String matchId}) async { Future<int?> getLatestRoundNumber({required String matchId}) async {
final query = selectOnly(scoreTable) final query = selectOnly(scoreEntryTable)
..where(scoreTable.matchId.equals(matchId)) ..where(scoreEntryTable.matchId.equals(matchId))
..addColumns([scoreTable.roundNumber.max()]); ..addColumns([scoreEntryTable.roundNumber.max()]);
final result = await query.getSingle(); final result = await query.getSingle();
return result.read(scoreTable.roundNumber.max()); return result.read(scoreEntryTable.roundNumber.max());
} }
/// Aggregates the total score for a player in a match by summing all their /// Aggregates the total score for a player in a match by summing all their
@@ -218,8 +221,8 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
deleteAllScoresForMatch(matchId: matchId); deleteAllScoresForMatch(matchId: matchId);
// Set the winner's score to 1 // Set the winner's score to 1
final rowsAffected = await into(scoreTable).insert( final rowsAffected = await into(scoreEntryTable).insert(
ScoreTableCompanion.insert( ScoreEntryTableCompanion.insert(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: 0, roundNumber: 0,
@@ -234,7 +237,7 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
// Retrieves the winner of a match based on the highest score. // Retrieves the winner of a match based on the highest score.
Future<Player?> getWinner({required String matchId}) async { Future<Player?> getWinner({required String matchId}) async {
final query = select(scoreTable) final query = select(scoreEntryTable)
..where((s) => s.matchId.equals(matchId)) ..where((s) => s.matchId.equals(matchId))
..orderBy([(s) => OrderingTerm.desc(s.score)]) ..orderBy([(s) => OrderingTerm.desc(s.score)])
..limit(1); ..limit(1);
@@ -278,8 +281,8 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
deleteAllScoresForMatch(matchId: matchId); deleteAllScoresForMatch(matchId: matchId);
// Set the loosers score to 0 // Set the loosers score to 0
final rowsAffected = await into(scoreTable).insert( final rowsAffected = await into(scoreEntryTable).insert(
ScoreTableCompanion.insert( ScoreEntryTableCompanion.insert(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: 0, roundNumber: 0,
@@ -294,7 +297,7 @@ class ScoreDao extends DatabaseAccessor<AppDatabase> with _$ScoreDaoMixin {
/// Retrieves the looser of a match based on the score 0. /// Retrieves the looser of a match based on the score 0.
Future<Player?> getLooser({required String matchId}) async { Future<Player?> getLooser({required String matchId}) async {
final query = select(scoreTable) final query = select(scoreEntryTable)
..where((s) => s.matchId.equals(matchId) & s.score.equals(0)); ..where((s) => s.matchId.equals(matchId) & s.score.equals(0));
final result = await query.getSingleOrNull(); final result = await query.getSingleOrNull();

View File

@@ -1,20 +1,20 @@
// GENERATED CODE - DO NOT MODIFY BY HAND // GENERATED CODE - DO NOT MODIFY BY HAND
part of 'score_dao.dart'; part of 'score_entry_dao.dart';
// ignore_for_file: type=lint // ignore_for_file: type=lint
mixin _$ScoreDaoMixin on DatabaseAccessor<AppDatabase> { mixin _$ScoreEntryDaoMixin on DatabaseAccessor<AppDatabase> {
$PlayerTableTable get playerTable => attachedDatabase.playerTable; $PlayerTableTable get playerTable => attachedDatabase.playerTable;
$GameTableTable get gameTable => attachedDatabase.gameTable; $GameTableTable get gameTable => attachedDatabase.gameTable;
$GroupTableTable get groupTable => attachedDatabase.groupTable; $GroupTableTable get groupTable => attachedDatabase.groupTable;
$MatchTableTable get matchTable => attachedDatabase.matchTable; $MatchTableTable get matchTable => attachedDatabase.matchTable;
$ScoreTableTable get scoreTable => attachedDatabase.scoreTable; $ScoreEntryTableTable get scoreEntryTable => attachedDatabase.scoreEntryTable;
ScoreDaoManager get managers => ScoreDaoManager(this); ScoreEntryDaoManager get managers => ScoreEntryDaoManager(this);
} }
class ScoreDaoManager { class ScoreEntryDaoManager {
final _$ScoreDaoMixin _db; final _$ScoreEntryDaoMixin _db;
ScoreDaoManager(this._db); ScoreEntryDaoManager(this._db);
$$PlayerTableTableTableManager get playerTable => $$PlayerTableTableTableManager get playerTable =>
$$PlayerTableTableTableManager(_db.attachedDatabase, _db.playerTable); $$PlayerTableTableTableManager(_db.attachedDatabase, _db.playerTable);
$$GameTableTableTableManager get gameTable => $$GameTableTableTableManager get gameTable =>
@@ -23,6 +23,9 @@ class ScoreDaoManager {
$$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable); $$GroupTableTableTableManager(_db.attachedDatabase, _db.groupTable);
$$MatchTableTableTableManager get matchTable => $$MatchTableTableTableManager get matchTable =>
$$MatchTableTableTableManager(_db.attachedDatabase, _db.matchTable); $$MatchTableTableTableManager(_db.attachedDatabase, _db.matchTable);
$$ScoreTableTableTableManager get scoreTable => $$ScoreEntryTableTableTableManager get scoreEntryTable =>
$$ScoreTableTableTableManager(_db.attachedDatabase, _db.scoreTable); $$ScoreEntryTableTableTableManager(
_db.attachedDatabase,
_db.scoreEntryTable,
);
} }

View File

@@ -7,7 +7,7 @@ import 'package:tallee/data/dao/match_dao.dart';
import 'package:tallee/data/dao/player_dao.dart'; import 'package:tallee/data/dao/player_dao.dart';
import 'package:tallee/data/dao/player_group_dao.dart'; import 'package:tallee/data/dao/player_group_dao.dart';
import 'package:tallee/data/dao/player_match_dao.dart'; import 'package:tallee/data/dao/player_match_dao.dart';
import 'package:tallee/data/dao/score_dao.dart'; import 'package:tallee/data/dao/score_entry_dao.dart';
import 'package:tallee/data/dao/team_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/game_table.dart';
import 'package:tallee/data/db/tables/group_table.dart'; import 'package:tallee/data/db/tables/group_table.dart';
@@ -15,7 +15,7 @@ import 'package:tallee/data/db/tables/match_table.dart';
import 'package:tallee/data/db/tables/player_group_table.dart'; import 'package:tallee/data/db/tables/player_group_table.dart';
import 'package:tallee/data/db/tables/player_match_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/player_table.dart';
import 'package:tallee/data/db/tables/score_table.dart'; import 'package:tallee/data/db/tables/score_entry_table.dart';
import 'package:tallee/data/db/tables/team_table.dart'; import 'package:tallee/data/db/tables/team_table.dart';
part 'database.g.dart'; part 'database.g.dart';
@@ -29,7 +29,7 @@ part 'database.g.dart';
PlayerMatchTable, PlayerMatchTable,
GameTable, GameTable,
TeamTable, TeamTable,
ScoreTable, ScoreEntryTable,
], ],
daos: [ daos: [
PlayerDao, PlayerDao,
@@ -38,8 +38,8 @@ part 'database.g.dart';
PlayerGroupDao, PlayerGroupDao,
PlayerMatchDao, PlayerMatchDao,
GameDao, GameDao,
ScoreDao, ScoreEntryDao,
TeamDao TeamDao,
], ],
) )
class AppDatabase extends _$AppDatabase { class AppDatabase extends _$AppDatabase {
@@ -60,7 +60,9 @@ class AppDatabase extends _$AppDatabase {
static QueryExecutor _openConnection() { static QueryExecutor _openConnection() {
return driftDatabase( return driftDatabase(
name: 'gametracker_db', name: 'gametracker_db',
native: const DriftNativeOptions(databaseDirectory: getApplicationSupportDirectory), native: const DriftNativeOptions(
databaseDirectory: getApplicationSupportDirectory,
),
); );
} }
} }

View File

@@ -2307,12 +2307,12 @@ class PlayerMatchTableCompanion extends UpdateCompanion<PlayerMatchTableData> {
} }
} }
class $ScoreTableTable extends ScoreTable class $ScoreEntryTableTable extends ScoreEntryTable
with TableInfo<$ScoreTableTable, ScoreTableData> { with TableInfo<$ScoreEntryTableTable, ScoreEntryTableData> {
@override @override
final GeneratedDatabase attachedDatabase; final GeneratedDatabase attachedDatabase;
final String? _alias; final String? _alias;
$ScoreTableTable(this.attachedDatabase, [this._alias]); $ScoreEntryTableTable(this.attachedDatabase, [this._alias]);
static const VerificationMeta _playerIdMeta = const VerificationMeta( static const VerificationMeta _playerIdMeta = const VerificationMeta(
'playerId', 'playerId',
); );
@@ -2382,10 +2382,10 @@ class $ScoreTableTable extends ScoreTable
String get aliasedName => _alias ?? actualTableName; String get aliasedName => _alias ?? actualTableName;
@override @override
String get actualTableName => $name; String get actualTableName => $name;
static const String $name = 'score_table'; static const String $name = 'score_entry_table';
@override @override
VerificationContext validateIntegrity( VerificationContext validateIntegrity(
Insertable<ScoreTableData> instance, { Insertable<ScoreEntryTableData> instance, {
bool isInserting = false, bool isInserting = false,
}) { }) {
final context = VerificationContext(); final context = VerificationContext();
@@ -2439,9 +2439,9 @@ class $ScoreTableTable extends ScoreTable
@override @override
Set<GeneratedColumn> get $primaryKey => {playerId, matchId, roundNumber}; Set<GeneratedColumn> get $primaryKey => {playerId, matchId, roundNumber};
@override @override
ScoreTableData map(Map<String, dynamic> data, {String? tablePrefix}) { ScoreEntryTableData map(Map<String, dynamic> data, {String? tablePrefix}) {
final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : ''; final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : '';
return ScoreTableData( return ScoreEntryTableData(
playerId: attachedDatabase.typeMapping.read( playerId: attachedDatabase.typeMapping.read(
DriftSqlType.string, DriftSqlType.string,
data['${effectivePrefix}player_id'], data['${effectivePrefix}player_id'],
@@ -2466,18 +2466,19 @@ class $ScoreTableTable extends ScoreTable
} }
@override @override
$ScoreTableTable createAlias(String alias) { $ScoreEntryTableTable createAlias(String alias) {
return $ScoreTableTable(attachedDatabase, alias); return $ScoreEntryTableTable(attachedDatabase, alias);
} }
} }
class ScoreTableData extends DataClass implements Insertable<ScoreTableData> { class ScoreEntryTableData extends DataClass
implements Insertable<ScoreEntryTableData> {
final String playerId; final String playerId;
final String matchId; final String matchId;
final int roundNumber; final int roundNumber;
final int score; final int score;
final int change; final int change;
const ScoreTableData({ const ScoreEntryTableData({
required this.playerId, required this.playerId,
required this.matchId, required this.matchId,
required this.roundNumber, required this.roundNumber,
@@ -2495,8 +2496,8 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
return map; return map;
} }
ScoreTableCompanion toCompanion(bool nullToAbsent) { ScoreEntryTableCompanion toCompanion(bool nullToAbsent) {
return ScoreTableCompanion( return ScoreEntryTableCompanion(
playerId: Value(playerId), playerId: Value(playerId),
matchId: Value(matchId), matchId: Value(matchId),
roundNumber: Value(roundNumber), roundNumber: Value(roundNumber),
@@ -2505,12 +2506,12 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
); );
} }
factory ScoreTableData.fromJson( factory ScoreEntryTableData.fromJson(
Map<String, dynamic> json, { Map<String, dynamic> json, {
ValueSerializer? serializer, ValueSerializer? serializer,
}) { }) {
serializer ??= driftRuntimeOptions.defaultSerializer; serializer ??= driftRuntimeOptions.defaultSerializer;
return ScoreTableData( return ScoreEntryTableData(
playerId: serializer.fromJson<String>(json['playerId']), playerId: serializer.fromJson<String>(json['playerId']),
matchId: serializer.fromJson<String>(json['matchId']), matchId: serializer.fromJson<String>(json['matchId']),
roundNumber: serializer.fromJson<int>(json['roundNumber']), roundNumber: serializer.fromJson<int>(json['roundNumber']),
@@ -2530,21 +2531,21 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
}; };
} }
ScoreTableData copyWith({ ScoreEntryTableData copyWith({
String? playerId, String? playerId,
String? matchId, String? matchId,
int? roundNumber, int? roundNumber,
int? score, int? score,
int? change, int? change,
}) => ScoreTableData( }) => ScoreEntryTableData(
playerId: playerId ?? this.playerId, playerId: playerId ?? this.playerId,
matchId: matchId ?? this.matchId, matchId: matchId ?? this.matchId,
roundNumber: roundNumber ?? this.roundNumber, roundNumber: roundNumber ?? this.roundNumber,
score: score ?? this.score, score: score ?? this.score,
change: change ?? this.change, change: change ?? this.change,
); );
ScoreTableData copyWithCompanion(ScoreTableCompanion data) { ScoreEntryTableData copyWithCompanion(ScoreEntryTableCompanion data) {
return ScoreTableData( return ScoreEntryTableData(
playerId: data.playerId.present ? data.playerId.value : this.playerId, playerId: data.playerId.present ? data.playerId.value : this.playerId,
matchId: data.matchId.present ? data.matchId.value : this.matchId, matchId: data.matchId.present ? data.matchId.value : this.matchId,
roundNumber: data.roundNumber.present roundNumber: data.roundNumber.present
@@ -2557,7 +2558,7 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
@override @override
String toString() { String toString() {
return (StringBuffer('ScoreTableData(') return (StringBuffer('ScoreEntryTableData(')
..write('playerId: $playerId, ') ..write('playerId: $playerId, ')
..write('matchId: $matchId, ') ..write('matchId: $matchId, ')
..write('roundNumber: $roundNumber, ') ..write('roundNumber: $roundNumber, ')
@@ -2573,7 +2574,7 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
@override @override
bool operator ==(Object other) => bool operator ==(Object other) =>
identical(this, other) || identical(this, other) ||
(other is ScoreTableData && (other is ScoreEntryTableData &&
other.playerId == this.playerId && other.playerId == this.playerId &&
other.matchId == this.matchId && other.matchId == this.matchId &&
other.roundNumber == this.roundNumber && other.roundNumber == this.roundNumber &&
@@ -2581,14 +2582,14 @@ class ScoreTableData extends DataClass implements Insertable<ScoreTableData> {
other.change == this.change); other.change == this.change);
} }
class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> { class ScoreEntryTableCompanion extends UpdateCompanion<ScoreEntryTableData> {
final Value<String> playerId; final Value<String> playerId;
final Value<String> matchId; final Value<String> matchId;
final Value<int> roundNumber; final Value<int> roundNumber;
final Value<int> score; final Value<int> score;
final Value<int> change; final Value<int> change;
final Value<int> rowid; final Value<int> rowid;
const ScoreTableCompanion({ const ScoreEntryTableCompanion({
this.playerId = const Value.absent(), this.playerId = const Value.absent(),
this.matchId = const Value.absent(), this.matchId = const Value.absent(),
this.roundNumber = const Value.absent(), this.roundNumber = const Value.absent(),
@@ -2596,7 +2597,7 @@ class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> {
this.change = const Value.absent(), this.change = const Value.absent(),
this.rowid = const Value.absent(), this.rowid = const Value.absent(),
}); });
ScoreTableCompanion.insert({ ScoreEntryTableCompanion.insert({
required String playerId, required String playerId,
required String matchId, required String matchId,
required int roundNumber, required int roundNumber,
@@ -2608,7 +2609,7 @@ class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> {
roundNumber = Value(roundNumber), roundNumber = Value(roundNumber),
score = Value(score), score = Value(score),
change = Value(change); change = Value(change);
static Insertable<ScoreTableData> custom({ static Insertable<ScoreEntryTableData> custom({
Expression<String>? playerId, Expression<String>? playerId,
Expression<String>? matchId, Expression<String>? matchId,
Expression<int>? roundNumber, Expression<int>? roundNumber,
@@ -2626,7 +2627,7 @@ class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> {
}); });
} }
ScoreTableCompanion copyWith({ ScoreEntryTableCompanion copyWith({
Value<String>? playerId, Value<String>? playerId,
Value<String>? matchId, Value<String>? matchId,
Value<int>? roundNumber, Value<int>? roundNumber,
@@ -2634,7 +2635,7 @@ class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> {
Value<int>? change, Value<int>? change,
Value<int>? rowid, Value<int>? rowid,
}) { }) {
return ScoreTableCompanion( return ScoreEntryTableCompanion(
playerId: playerId ?? this.playerId, playerId: playerId ?? this.playerId,
matchId: matchId ?? this.matchId, matchId: matchId ?? this.matchId,
roundNumber: roundNumber ?? this.roundNumber, roundNumber: roundNumber ?? this.roundNumber,
@@ -2670,7 +2671,7 @@ class ScoreTableCompanion extends UpdateCompanion<ScoreTableData> {
@override @override
String toString() { String toString() {
return (StringBuffer('ScoreTableCompanion(') return (StringBuffer('ScoreEntryTableCompanion(')
..write('playerId: $playerId, ') ..write('playerId: $playerId, ')
..write('matchId: $matchId, ') ..write('matchId: $matchId, ')
..write('roundNumber: $roundNumber, ') ..write('roundNumber: $roundNumber, ')
@@ -2696,7 +2697,9 @@ abstract class _$AppDatabase extends GeneratedDatabase {
late final $PlayerMatchTableTable playerMatchTable = $PlayerMatchTableTable( late final $PlayerMatchTableTable playerMatchTable = $PlayerMatchTableTable(
this, this,
); );
late final $ScoreTableTable scoreTable = $ScoreTableTable(this); late final $ScoreEntryTableTable scoreEntryTable = $ScoreEntryTableTable(
this,
);
late final PlayerDao playerDao = PlayerDao(this as AppDatabase); late final PlayerDao playerDao = PlayerDao(this as AppDatabase);
late final GroupDao groupDao = GroupDao(this as AppDatabase); late final GroupDao groupDao = GroupDao(this as AppDatabase);
late final MatchDao matchDao = MatchDao(this as AppDatabase); late final MatchDao matchDao = MatchDao(this as AppDatabase);
@@ -2707,7 +2710,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
this as AppDatabase, this as AppDatabase,
); );
late final GameDao gameDao = GameDao(this as AppDatabase); late final GameDao gameDao = GameDao(this as AppDatabase);
late final ScoreDao scoreDao = ScoreDao(this as AppDatabase); late final ScoreEntryDao scoreEntryDao = ScoreEntryDao(this as AppDatabase);
late final TeamDao teamDao = TeamDao(this as AppDatabase); late final TeamDao teamDao = TeamDao(this as AppDatabase);
@override @override
Iterable<TableInfo<Table, Object?>> get allTables => Iterable<TableInfo<Table, Object?>> get allTables =>
@@ -2721,7 +2724,7 @@ abstract class _$AppDatabase extends GeneratedDatabase {
playerGroupTable, playerGroupTable,
teamTable, teamTable,
playerMatchTable, playerMatchTable,
scoreTable, scoreEntryTable,
]; ];
@override @override
StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([ StreamQueryUpdateRules get streamUpdateRules => const StreamQueryUpdateRules([
@@ -2772,14 +2775,14 @@ abstract class _$AppDatabase extends GeneratedDatabase {
'player_table', 'player_table',
limitUpdateKind: UpdateKind.delete, limitUpdateKind: UpdateKind.delete,
), ),
result: [TableUpdate('score_table', kind: UpdateKind.delete)], result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)],
), ),
WritePropagation( WritePropagation(
on: TableUpdateQuery.onTableName( on: TableUpdateQuery.onTableName(
'match_table', 'match_table',
limitUpdateKind: UpdateKind.delete, limitUpdateKind: UpdateKind.delete,
), ),
result: [TableUpdate('score_table', kind: UpdateKind.delete)], result: [TableUpdate('score_entry_table', kind: UpdateKind.delete)],
), ),
]); ]);
} }
@@ -2851,19 +2854,24 @@ final class $$PlayerTableTableReferences
); );
} }
static MultiTypedResultKey<$ScoreTableTable, List<ScoreTableData>> static MultiTypedResultKey<$ScoreEntryTableTable, List<ScoreEntryTableData>>
_scoreTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( _scoreEntryTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable(
db.scoreTable, db.scoreEntryTable,
aliasName: $_aliasNameGenerator(db.playerTable.id, db.scoreTable.playerId), aliasName: $_aliasNameGenerator(
db.playerTable.id,
db.scoreEntryTable.playerId,
),
); );
$$ScoreTableTableProcessedTableManager get scoreTableRefs { $$ScoreEntryTableTableProcessedTableManager get scoreEntryTableRefs {
final manager = $$ScoreTableTableTableManager( final manager = $$ScoreEntryTableTableTableManager(
$_db, $_db,
$_db.scoreTable, $_db.scoreEntryTable,
).filter((f) => f.playerId.id.sqlEquals($_itemColumn<String>('id')!)); ).filter((f) => f.playerId.id.sqlEquals($_itemColumn<String>('id')!));
final cache = $_typedResult.readTableOrNull(_scoreTableRefsTable($_db)); final cache = $_typedResult.readTableOrNull(
_scoreEntryTableRefsTable($_db),
);
return ProcessedTableManager( return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache), manager.$state.copyWith(prefetchedData: cache),
); );
@@ -2949,22 +2957,22 @@ class $$PlayerTableTableFilterComposer
return f(composer); return f(composer);
} }
Expression<bool> scoreTableRefs( Expression<bool> scoreEntryTableRefs(
Expression<bool> Function($$ScoreTableTableFilterComposer f) f, Expression<bool> Function($$ScoreEntryTableTableFilterComposer f) f,
) { ) {
final $$ScoreTableTableFilterComposer composer = $composerBuilder( final $$ScoreEntryTableTableFilterComposer composer = $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.id, getCurrentColumn: (t) => t.id,
referencedTable: $db.scoreTable, referencedTable: $db.scoreEntryTable,
getReferencedColumn: (t) => t.playerId, getReferencedColumn: (t) => t.playerId,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => $$ScoreTableTableFilterComposer( }) => $$ScoreEntryTableTableFilterComposer(
$db: $db, $db: $db,
$table: $db.scoreTable, $table: $db.scoreEntryTable,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -3078,22 +3086,22 @@ class $$PlayerTableTableAnnotationComposer
return f(composer); return f(composer);
} }
Expression<T> scoreTableRefs<T extends Object>( Expression<T> scoreEntryTableRefs<T extends Object>(
Expression<T> Function($$ScoreTableTableAnnotationComposer a) f, Expression<T> Function($$ScoreEntryTableTableAnnotationComposer a) f,
) { ) {
final $$ScoreTableTableAnnotationComposer composer = $composerBuilder( final $$ScoreEntryTableTableAnnotationComposer composer = $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.id, getCurrentColumn: (t) => t.id,
referencedTable: $db.scoreTable, referencedTable: $db.scoreEntryTable,
getReferencedColumn: (t) => t.playerId, getReferencedColumn: (t) => t.playerId,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => $$ScoreTableTableAnnotationComposer( }) => $$ScoreEntryTableTableAnnotationComposer(
$db: $db, $db: $db,
$table: $db.scoreTable, $table: $db.scoreEntryTable,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -3120,7 +3128,7 @@ class $$PlayerTableTableTableManager
PrefetchHooks Function({ PrefetchHooks Function({
bool playerGroupTableRefs, bool playerGroupTableRefs,
bool playerMatchTableRefs, bool playerMatchTableRefs,
bool scoreTableRefs, bool scoreEntryTableRefs,
}) })
> { > {
$$PlayerTableTableTableManager(_$AppDatabase db, $PlayerTableTable table) $$PlayerTableTableTableManager(_$AppDatabase db, $PlayerTableTable table)
@@ -3174,14 +3182,14 @@ class $$PlayerTableTableTableManager
({ ({
playerGroupTableRefs = false, playerGroupTableRefs = false,
playerMatchTableRefs = false, playerMatchTableRefs = false,
scoreTableRefs = false, scoreEntryTableRefs = false,
}) { }) {
return PrefetchHooks( return PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [ explicitlyWatchedTables: [
if (playerGroupTableRefs) db.playerGroupTable, if (playerGroupTableRefs) db.playerGroupTable,
if (playerMatchTableRefs) db.playerMatchTable, if (playerMatchTableRefs) db.playerMatchTable,
if (scoreTableRefs) db.scoreTable, if (scoreEntryTableRefs) db.scoreEntryTable,
], ],
addJoins: null, addJoins: null,
getPrefetchedDataCallback: (items) async { getPrefetchedDataCallback: (items) async {
@@ -3228,21 +3236,21 @@ class $$PlayerTableTableTableManager
), ),
typedResults: items, typedResults: items,
), ),
if (scoreTableRefs) if (scoreEntryTableRefs)
await $_getPrefetchedData< await $_getPrefetchedData<
PlayerTableData, PlayerTableData,
$PlayerTableTable, $PlayerTableTable,
ScoreTableData ScoreEntryTableData
>( >(
currentTable: table, currentTable: table,
referencedTable: $$PlayerTableTableReferences referencedTable: $$PlayerTableTableReferences
._scoreTableRefsTable(db), ._scoreEntryTableRefsTable(db),
managerFromTypedResult: (p0) => managerFromTypedResult: (p0) =>
$$PlayerTableTableReferences( $$PlayerTableTableReferences(
db, db,
table, table,
p0, p0,
).scoreTableRefs, ).scoreEntryTableRefs,
referencedItemsForCurrentItem: referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems.where( (item, referencedItems) => referencedItems.where(
(e) => e.playerId == item.id, (e) => e.playerId == item.id,
@@ -3272,7 +3280,7 @@ typedef $$PlayerTableTableProcessedTableManager =
PrefetchHooks Function({ PrefetchHooks Function({
bool playerGroupTableRefs, bool playerGroupTableRefs,
bool playerMatchTableRefs, bool playerMatchTableRefs,
bool scoreTableRefs, bool scoreEntryTableRefs,
}) })
>; >;
typedef $$GroupTableTableCreateCompanionBuilder = typedef $$GroupTableTableCreateCompanionBuilder =
@@ -4089,19 +4097,24 @@ final class $$MatchTableTableReferences
); );
} }
static MultiTypedResultKey<$ScoreTableTable, List<ScoreTableData>> static MultiTypedResultKey<$ScoreEntryTableTable, List<ScoreEntryTableData>>
_scoreTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable( _scoreEntryTableRefsTable(_$AppDatabase db) => MultiTypedResultKey.fromTable(
db.scoreTable, db.scoreEntryTable,
aliasName: $_aliasNameGenerator(db.matchTable.id, db.scoreTable.matchId), aliasName: $_aliasNameGenerator(
db.matchTable.id,
db.scoreEntryTable.matchId,
),
); );
$$ScoreTableTableProcessedTableManager get scoreTableRefs { $$ScoreEntryTableTableProcessedTableManager get scoreEntryTableRefs {
final manager = $$ScoreTableTableTableManager( final manager = $$ScoreEntryTableTableTableManager(
$_db, $_db,
$_db.scoreTable, $_db.scoreEntryTable,
).filter((f) => f.matchId.id.sqlEquals($_itemColumn<String>('id')!)); ).filter((f) => f.matchId.id.sqlEquals($_itemColumn<String>('id')!));
final cache = $_typedResult.readTableOrNull(_scoreTableRefsTable($_db)); final cache = $_typedResult.readTableOrNull(
_scoreEntryTableRefsTable($_db),
);
return ProcessedTableManager( return ProcessedTableManager(
manager.$state.copyWith(prefetchedData: cache), manager.$state.copyWith(prefetchedData: cache),
); );
@@ -4213,22 +4226,22 @@ class $$MatchTableTableFilterComposer
return f(composer); return f(composer);
} }
Expression<bool> scoreTableRefs( Expression<bool> scoreEntryTableRefs(
Expression<bool> Function($$ScoreTableTableFilterComposer f) f, Expression<bool> Function($$ScoreEntryTableTableFilterComposer f) f,
) { ) {
final $$ScoreTableTableFilterComposer composer = $composerBuilder( final $$ScoreEntryTableTableFilterComposer composer = $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.id, getCurrentColumn: (t) => t.id,
referencedTable: $db.scoreTable, referencedTable: $db.scoreEntryTable,
getReferencedColumn: (t) => t.matchId, getReferencedColumn: (t) => t.matchId,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => $$ScoreTableTableFilterComposer( }) => $$ScoreEntryTableTableFilterComposer(
$db: $db, $db: $db,
$table: $db.scoreTable, $table: $db.scoreEntryTable,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -4415,22 +4428,22 @@ class $$MatchTableTableAnnotationComposer
return f(composer); return f(composer);
} }
Expression<T> scoreTableRefs<T extends Object>( Expression<T> scoreEntryTableRefs<T extends Object>(
Expression<T> Function($$ScoreTableTableAnnotationComposer a) f, Expression<T> Function($$ScoreEntryTableTableAnnotationComposer a) f,
) { ) {
final $$ScoreTableTableAnnotationComposer composer = $composerBuilder( final $$ScoreEntryTableTableAnnotationComposer composer = $composerBuilder(
composer: this, composer: this,
getCurrentColumn: (t) => t.id, getCurrentColumn: (t) => t.id,
referencedTable: $db.scoreTable, referencedTable: $db.scoreEntryTable,
getReferencedColumn: (t) => t.matchId, getReferencedColumn: (t) => t.matchId,
builder: builder:
( (
joinBuilder, { joinBuilder, {
$addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer,
$removeJoinBuilderFromRootComposer, $removeJoinBuilderFromRootComposer,
}) => $$ScoreTableTableAnnotationComposer( }) => $$ScoreEntryTableTableAnnotationComposer(
$db: $db, $db: $db,
$table: $db.scoreTable, $table: $db.scoreEntryTable,
$addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer, $addJoinBuilderToRootComposer: $addJoinBuilderToRootComposer,
joinBuilder: joinBuilder, joinBuilder: joinBuilder,
$removeJoinBuilderFromRootComposer: $removeJoinBuilderFromRootComposer:
@@ -4458,7 +4471,7 @@ class $$MatchTableTableTableManager
bool gameId, bool gameId,
bool groupId, bool groupId,
bool playerMatchTableRefs, bool playerMatchTableRefs,
bool scoreTableRefs, bool scoreEntryTableRefs,
}) })
> { > {
$$MatchTableTableTableManager(_$AppDatabase db, $MatchTableTable table) $$MatchTableTableTableManager(_$AppDatabase db, $MatchTableTable table)
@@ -4525,13 +4538,13 @@ class $$MatchTableTableTableManager
gameId = false, gameId = false,
groupId = false, groupId = false,
playerMatchTableRefs = false, playerMatchTableRefs = false,
scoreTableRefs = false, scoreEntryTableRefs = false,
}) { }) {
return PrefetchHooks( return PrefetchHooks(
db: db, db: db,
explicitlyWatchedTables: [ explicitlyWatchedTables: [
if (playerMatchTableRefs) db.playerMatchTable, if (playerMatchTableRefs) db.playerMatchTable,
if (scoreTableRefs) db.scoreTable, if (scoreEntryTableRefs) db.scoreEntryTable,
], ],
addJoins: addJoins:
< <
@@ -4603,21 +4616,21 @@ class $$MatchTableTableTableManager
), ),
typedResults: items, typedResults: items,
), ),
if (scoreTableRefs) if (scoreEntryTableRefs)
await $_getPrefetchedData< await $_getPrefetchedData<
MatchTableData, MatchTableData,
$MatchTableTable, $MatchTableTable,
ScoreTableData ScoreEntryTableData
>( >(
currentTable: table, currentTable: table,
referencedTable: $$MatchTableTableReferences referencedTable: $$MatchTableTableReferences
._scoreTableRefsTable(db), ._scoreEntryTableRefsTable(db),
managerFromTypedResult: (p0) => managerFromTypedResult: (p0) =>
$$MatchTableTableReferences( $$MatchTableTableReferences(
db, db,
table, table,
p0, p0,
).scoreTableRefs, ).scoreEntryTableRefs,
referencedItemsForCurrentItem: referencedItemsForCurrentItem:
(item, referencedItems) => referencedItems.where( (item, referencedItems) => referencedItems.where(
(e) => e.matchId == item.id, (e) => e.matchId == item.id,
@@ -4648,7 +4661,7 @@ typedef $$MatchTableTableProcessedTableManager =
bool gameId, bool gameId,
bool groupId, bool groupId,
bool playerMatchTableRefs, bool playerMatchTableRefs,
bool scoreTableRefs, bool scoreEntryTableRefs,
}) })
>; >;
typedef $$PlayerGroupTableTableCreateCompanionBuilder = typedef $$PlayerGroupTableTableCreateCompanionBuilder =
@@ -5761,8 +5774,8 @@ typedef $$PlayerMatchTableTableProcessedTableManager =
PlayerMatchTableData, PlayerMatchTableData,
PrefetchHooks Function({bool playerId, bool matchId, bool teamId}) PrefetchHooks Function({bool playerId, bool matchId, bool teamId})
>; >;
typedef $$ScoreTableTableCreateCompanionBuilder = typedef $$ScoreEntryTableTableCreateCompanionBuilder =
ScoreTableCompanion Function({ ScoreEntryTableCompanion Function({
required String playerId, required String playerId,
required String matchId, required String matchId,
required int roundNumber, required int roundNumber,
@@ -5770,8 +5783,8 @@ typedef $$ScoreTableTableCreateCompanionBuilder =
required int change, required int change,
Value<int> rowid, Value<int> rowid,
}); });
typedef $$ScoreTableTableUpdateCompanionBuilder = typedef $$ScoreEntryTableTableUpdateCompanionBuilder =
ScoreTableCompanion Function({ ScoreEntryTableCompanion Function({
Value<String> playerId, Value<String> playerId,
Value<String> matchId, Value<String> matchId,
Value<int> roundNumber, Value<int> roundNumber,
@@ -5780,13 +5793,22 @@ typedef $$ScoreTableTableUpdateCompanionBuilder =
Value<int> rowid, Value<int> rowid,
}); });
final class $$ScoreTableTableReferences final class $$ScoreEntryTableTableReferences
extends BaseReferences<_$AppDatabase, $ScoreTableTable, ScoreTableData> { extends
$$ScoreTableTableReferences(super.$_db, super.$_table, super.$_typedResult); BaseReferences<
_$AppDatabase,
$ScoreEntryTableTable,
ScoreEntryTableData
> {
$$ScoreEntryTableTableReferences(
super.$_db,
super.$_table,
super.$_typedResult,
);
static $PlayerTableTable _playerIdTable(_$AppDatabase db) => static $PlayerTableTable _playerIdTable(_$AppDatabase db) =>
db.playerTable.createAlias( db.playerTable.createAlias(
$_aliasNameGenerator(db.scoreTable.playerId, db.playerTable.id), $_aliasNameGenerator(db.scoreEntryTable.playerId, db.playerTable.id),
); );
$$PlayerTableTableProcessedTableManager get playerId { $$PlayerTableTableProcessedTableManager get playerId {
@@ -5805,7 +5827,7 @@ final class $$ScoreTableTableReferences
static $MatchTableTable _matchIdTable(_$AppDatabase db) => static $MatchTableTable _matchIdTable(_$AppDatabase db) =>
db.matchTable.createAlias( db.matchTable.createAlias(
$_aliasNameGenerator(db.scoreTable.matchId, db.matchTable.id), $_aliasNameGenerator(db.scoreEntryTable.matchId, db.matchTable.id),
); );
$$MatchTableTableProcessedTableManager get matchId { $$MatchTableTableProcessedTableManager get matchId {
@@ -5823,9 +5845,9 @@ final class $$ScoreTableTableReferences
} }
} }
class $$ScoreTableTableFilterComposer class $$ScoreEntryTableTableFilterComposer
extends Composer<_$AppDatabase, $ScoreTableTable> { extends Composer<_$AppDatabase, $ScoreEntryTableTable> {
$$ScoreTableTableFilterComposer({ $$ScoreEntryTableTableFilterComposer({
required super.$db, required super.$db,
required super.$table, required super.$table,
super.joinBuilder, super.joinBuilder,
@@ -5894,9 +5916,9 @@ class $$ScoreTableTableFilterComposer
} }
} }
class $$ScoreTableTableOrderingComposer class $$ScoreEntryTableTableOrderingComposer
extends Composer<_$AppDatabase, $ScoreTableTable> { extends Composer<_$AppDatabase, $ScoreEntryTableTable> {
$$ScoreTableTableOrderingComposer({ $$ScoreEntryTableTableOrderingComposer({
required super.$db, required super.$db,
required super.$table, required super.$table,
super.joinBuilder, super.joinBuilder,
@@ -5965,9 +5987,9 @@ class $$ScoreTableTableOrderingComposer
} }
} }
class $$ScoreTableTableAnnotationComposer class $$ScoreEntryTableTableAnnotationComposer
extends Composer<_$AppDatabase, $ScoreTableTable> { extends Composer<_$AppDatabase, $ScoreEntryTableTable> {
$$ScoreTableTableAnnotationComposer({ $$ScoreEntryTableTableAnnotationComposer({
required super.$db, required super.$db,
required super.$table, required super.$table,
super.joinBuilder, super.joinBuilder,
@@ -6032,32 +6054,34 @@ class $$ScoreTableTableAnnotationComposer
} }
} }
class $$ScoreTableTableTableManager class $$ScoreEntryTableTableTableManager
extends extends
RootTableManager< RootTableManager<
_$AppDatabase, _$AppDatabase,
$ScoreTableTable, $ScoreEntryTableTable,
ScoreTableData, ScoreEntryTableData,
$$ScoreTableTableFilterComposer, $$ScoreEntryTableTableFilterComposer,
$$ScoreTableTableOrderingComposer, $$ScoreEntryTableTableOrderingComposer,
$$ScoreTableTableAnnotationComposer, $$ScoreEntryTableTableAnnotationComposer,
$$ScoreTableTableCreateCompanionBuilder, $$ScoreEntryTableTableCreateCompanionBuilder,
$$ScoreTableTableUpdateCompanionBuilder, $$ScoreEntryTableTableUpdateCompanionBuilder,
(ScoreTableData, $$ScoreTableTableReferences), (ScoreEntryTableData, $$ScoreEntryTableTableReferences),
ScoreTableData, ScoreEntryTableData,
PrefetchHooks Function({bool playerId, bool matchId}) PrefetchHooks Function({bool playerId, bool matchId})
> { > {
$$ScoreTableTableTableManager(_$AppDatabase db, $ScoreTableTable table) $$ScoreEntryTableTableTableManager(
: super( _$AppDatabase db,
$ScoreEntryTableTable table,
) : super(
TableManagerState( TableManagerState(
db: db, db: db,
table: table, table: table,
createFilteringComposer: () => createFilteringComposer: () =>
$$ScoreTableTableFilterComposer($db: db, $table: table), $$ScoreEntryTableTableFilterComposer($db: db, $table: table),
createOrderingComposer: () => createOrderingComposer: () =>
$$ScoreTableTableOrderingComposer($db: db, $table: table), $$ScoreEntryTableTableOrderingComposer($db: db, $table: table),
createComputedFieldComposer: () => createComputedFieldComposer: () =>
$$ScoreTableTableAnnotationComposer($db: db, $table: table), $$ScoreEntryTableTableAnnotationComposer($db: db, $table: table),
updateCompanionCallback: updateCompanionCallback:
({ ({
Value<String> playerId = const Value.absent(), Value<String> playerId = const Value.absent(),
@@ -6066,7 +6090,7 @@ class $$ScoreTableTableTableManager
Value<int> score = const Value.absent(), Value<int> score = const Value.absent(),
Value<int> change = const Value.absent(), Value<int> change = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => ScoreTableCompanion( }) => ScoreEntryTableCompanion(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: roundNumber, roundNumber: roundNumber,
@@ -6082,7 +6106,7 @@ class $$ScoreTableTableTableManager
required int score, required int score,
required int change, required int change,
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => ScoreTableCompanion.insert( }) => ScoreEntryTableCompanion.insert(
playerId: playerId, playerId: playerId,
matchId: matchId, matchId: matchId,
roundNumber: roundNumber, roundNumber: roundNumber,
@@ -6094,7 +6118,7 @@ class $$ScoreTableTableTableManager
.map( .map(
(e) => ( (e) => (
e.readTable(table), e.readTable(table),
$$ScoreTableTableReferences(db, table, e), $$ScoreEntryTableTableReferences(db, table, e),
), ),
) )
.toList(), .toList(),
@@ -6123,11 +6147,13 @@ class $$ScoreTableTableTableManager
state.withJoin( state.withJoin(
currentTable: table, currentTable: table,
currentColumn: table.playerId, currentColumn: table.playerId,
referencedTable: $$ScoreTableTableReferences referencedTable:
._playerIdTable(db), $$ScoreEntryTableTableReferences
referencedColumn: $$ScoreTableTableReferences ._playerIdTable(db),
._playerIdTable(db) referencedColumn:
.id, $$ScoreEntryTableTableReferences
._playerIdTable(db)
.id,
) )
as T; as T;
} }
@@ -6136,11 +6162,13 @@ class $$ScoreTableTableTableManager
state.withJoin( state.withJoin(
currentTable: table, currentTable: table,
currentColumn: table.matchId, currentColumn: table.matchId,
referencedTable: $$ScoreTableTableReferences referencedTable:
._matchIdTable(db), $$ScoreEntryTableTableReferences
referencedColumn: $$ScoreTableTableReferences ._matchIdTable(db),
._matchIdTable(db) referencedColumn:
.id, $$ScoreEntryTableTableReferences
._matchIdTable(db)
.id,
) )
as T; as T;
} }
@@ -6156,18 +6184,18 @@ class $$ScoreTableTableTableManager
); );
} }
typedef $$ScoreTableTableProcessedTableManager = typedef $$ScoreEntryTableTableProcessedTableManager =
ProcessedTableManager< ProcessedTableManager<
_$AppDatabase, _$AppDatabase,
$ScoreTableTable, $ScoreEntryTableTable,
ScoreTableData, ScoreEntryTableData,
$$ScoreTableTableFilterComposer, $$ScoreEntryTableTableFilterComposer,
$$ScoreTableTableOrderingComposer, $$ScoreEntryTableTableOrderingComposer,
$$ScoreTableTableAnnotationComposer, $$ScoreEntryTableTableAnnotationComposer,
$$ScoreTableTableCreateCompanionBuilder, $$ScoreEntryTableTableCreateCompanionBuilder,
$$ScoreTableTableUpdateCompanionBuilder, $$ScoreEntryTableTableUpdateCompanionBuilder,
(ScoreTableData, $$ScoreTableTableReferences), (ScoreEntryTableData, $$ScoreEntryTableTableReferences),
ScoreTableData, ScoreEntryTableData,
PrefetchHooks Function({bool playerId, bool matchId}) PrefetchHooks Function({bool playerId, bool matchId})
>; >;
@@ -6188,6 +6216,6 @@ class $AppDatabaseManager {
$$TeamTableTableTableManager(_db, _db.teamTable); $$TeamTableTableTableManager(_db, _db.teamTable);
$$PlayerMatchTableTableTableManager get playerMatchTable => $$PlayerMatchTableTableTableManager get playerMatchTable =>
$$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable); $$PlayerMatchTableTableTableManager(_db, _db.playerMatchTable);
$$ScoreTableTableTableManager get scoreTable => $$ScoreEntryTableTableTableManager get scoreEntryTable =>
$$ScoreTableTableTableManager(_db, _db.scoreTable); $$ScoreEntryTableTableTableManager(_db, _db.scoreEntryTable);
} }

View File

@@ -2,7 +2,7 @@ import 'package:drift/drift.dart';
import 'package:tallee/data/db/tables/match_table.dart'; import 'package:tallee/data/db/tables/match_table.dart';
import 'package:tallee/data/db/tables/player_table.dart'; import 'package:tallee/data/db/tables/player_table.dart';
class ScoreTable extends Table { class ScoreEntryTable extends Table {
TextColumn get playerId => TextColumn get playerId =>
text().references(PlayerTable, #id, onDelete: KeyAction.cascade)(); text().references(PlayerTable, #id, onDelete: KeyAction.cascade)();
TextColumn get matchId => TextColumn get matchId =>

View File

@@ -24,16 +24,17 @@ class Group {
return 'Group{id: $id, name: $name, description: $description, members: $members}'; return 'Group{id: $id, name: $name, description: $description, members: $members}';
} }
/// Creates a Group instance from a JSON object (memberIds format). /// Creates a Group instance from a JSON object where the related [Player]
/// Player objects are reconstructed from memberIds by the DataTransferService. /// objects are represented by their IDs.
Group.fromJson(Map<String, dynamic> json) Group.fromJson(Map<String, dynamic> json)
: id = json['id'], : id = json['id'],
createdAt = DateTime.parse(json['createdAt']), createdAt = DateTime.parse(json['createdAt']),
name = json['name'], name = json['name'],
description = json['description'], description = json['description'],
members = []; // Populated during import via DataTransferService members = [];
/// Converts the Group instance to a JSON object using normalized format (memberIds only). /// Converts the Group instance to a JSON object. Related [Player] objects are
/// represented by their IDs.
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'createdAt': createdAt.toIso8601String(), 'createdAt': createdAt.toIso8601String(),

View File

@@ -3,7 +3,7 @@ import 'package:tallee/core/enums.dart';
import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score.dart'; import 'package:tallee/data/models/score_entry.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class Match { class Match {
@@ -15,7 +15,7 @@ class Match {
final Group? group; final Group? group;
final List<Player> players; final List<Player> players;
final String notes; final String notes;
Map<String, List<Score>> scores; Map<String, List<ScoreEntry>> scores;
Player? winner; Player? winner;
Match({ Match({
@@ -27,7 +27,7 @@ class Match {
this.group, this.group,
this.players = const [], this.players = const [],
this.notes = '', this.notes = '',
Map<String, List<Score>>? scores, Map<String, List<ScoreEntry>>? scores,
this.winner, this.winner,
}) : id = id ?? const Uuid().v4(), }) : id = id ?? const Uuid().v4(),
createdAt = createdAt ?? clock.now(), createdAt = createdAt ?? clock.now(),
@@ -38,8 +38,9 @@ class Match {
return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, winner: $winner}'; return 'Match{id: $id, createdAt: $createdAt, endedAt: $endedAt, name: $name, game: $game, group: $group, players: $players, notes: $notes, scores: $scores, winner: $winner}';
} }
/// Creates a Match instance from a JSON object (ID references format). /// Creates a Match instance from a JSON object where related objects are
/// Related objects are reconstructed from IDs by the DataTransferService. /// represented by their IDs. Therefore, the game, group, and players are not
/// fully constructed here.
Match.fromJson(Map<String, dynamic> json) Match.fromJson(Map<String, dynamic> json)
: id = json['id'], : id = json['id'],
createdAt = DateTime.parse(json['createdAt']), createdAt = DateTime.parse(json['createdAt']),
@@ -53,13 +54,15 @@ class Match {
description: '', description: '',
color: GameColor.blue, color: GameColor.blue,
icon: '', icon: '',
), // Populated during import via DataTransferService ),
group = null, // Populated during import via DataTransferService group = null,
players = [], // Populated during import via DataTransferService players = [],
scores = json['scores'], scores = json['scores'],
notes = json['notes'] ?? ''; notes = json['notes'] ?? '';
/// Converts the Match instance to a JSON object using normalized format (ID references only). /// Converts the Match instance to a JSON object. Related objects are
/// represented by their IDs, so the game, group, and players are not fully
/// serialized here.
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'createdAt': createdAt.toIso8601String(), 'createdAt': createdAt.toIso8601String(),

View File

@@ -1,18 +0,0 @@
class Score {
final int roundNumber;
int score = 0;
int change = 0;
Score({required this.roundNumber, required this.score, required this.change});
Score.fromJson(Map<String, dynamic> json)
: roundNumber = json['roundNumber'],
score = json['score'],
change = json['change'];
Map<String, dynamic> toJson() => {
'roundNumber': roundNumber,
'score': score,
'change': change,
};
}

View File

@@ -0,0 +1,22 @@
class ScoreEntry {
int roundNumber = 0;
final int score;
final int change;
ScoreEntry({
required this.roundNumber,
required this.score,
required this.change,
});
ScoreEntry.fromJson(Map<String, dynamic> json)
: roundNumber = json['roundNumber'],
score = json['score'],
change = json['change'];
Map<String, dynamic> toJson() => {
'roundNumber': roundNumber,
'score': score,
'change': change,
};
}

View File

@@ -29,7 +29,8 @@ class Team {
createdAt = DateTime.parse(json['createdAt']), createdAt = DateTime.parse(json['createdAt']),
members = []; // Populated during import via DataTransferService members = []; // Populated during import via DataTransferService
/// Converts the Team instance to a JSON object using normalized format (memberIds only). /// Converts the Team instance to a JSON object. Related objects are
/// represented by their IDs.
Map<String, dynamic> toJson() => { Map<String, dynamic> toJson() => {
'id': id, 'id': id,
'name': name, 'name': name,

View File

@@ -22,10 +22,11 @@
"days_ago": "vor {count} Tagen", "days_ago": "vor {count} Tagen",
"delete": "Löschen", "delete": "Löschen",
"delete_all_data": "Alle Daten löschen", "delete_all_data": "Alle Daten löschen",
"delete_group": "Diese Gruppe löschen", "delete_group": "Gruppe löschen",
"delete_match": "Spiel löschen", "delete_match": "Spiel löschen",
"edit_group": "Gruppe bearbeiten", "edit_group": "Gruppe bearbeiten",
"edit_match": "Gruppe bearbeiten", "edit_match": "Gruppe bearbeiten",
"enter_points": "Punkte eingeben",
"enter_results": "Ergebnisse eintragen", "enter_results": "Ergebnisse eintragen",
"error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen", "error_creating_group": "Fehler beim Erstellen der Gruppe, bitte erneut versuchen",
"error_deleting_group": "Fehler beim Löschen der Gruppe, bitte erneut versuchen", "error_deleting_group": "Fehler beim Löschen der Gruppe, bitte erneut versuchen",
@@ -74,6 +75,7 @@
"player_name": "Spieler:innenname", "player_name": "Spieler:innenname",
"players": "Spieler:innen", "players": "Spieler:innen",
"players_count": "{count} Spieler", "players_count": "{count} Spieler",
"points": "Punkte",
"privacy_policy": "Datenschutzerklärung", "privacy_policy": "Datenschutzerklärung",
"quick_create": "Schnellzugriff", "quick_create": "Schnellzugriff",
"recent_matches": "Letzte Spiele", "recent_matches": "Letzte Spiele",
@@ -87,12 +89,14 @@
"save_changes": "Änderungen speichern", "save_changes": "Änderungen speichern",
"search_for_groups": "Nach Gruppen suchen", "search_for_groups": "Nach Gruppen suchen",
"search_for_players": "Nach Spieler:innen suchen", "search_for_players": "Nach Spieler:innen suchen",
"select_winner": "Gewinner:in wählen:", "select_winner": "Gewinner:in wählen",
"select_loser": "Verlierer:in wählen",
"selected_players": "Ausgewählte Spieler:innen", "selected_players": "Ausgewählte Spieler:innen",
"settings": "Einstellungen", "settings": "Einstellungen",
"single_loser": "Ein:e Verlierer:in", "single_loser": "Ein:e Verlierer:in",
"single_winner": "Ein:e Gewinner:in", "single_winner": "Ein:e Gewinner:in",
"highest_score": "Höchste Punkte", "highest_score": "Höchste Punkte",
"loser": "Verlierer:in",
"lowest_score": "Niedrigste Punkte", "lowest_score": "Niedrigste Punkte",
"multiple_winners": "Mehrere Gewinner:innen", "multiple_winners": "Mehrere Gewinner:innen",
"statistics": "Statistiken", "statistics": "Statistiken",

View File

@@ -83,6 +83,9 @@
"@edit_match": { "@edit_match": {
"description": "Button & Appbar label for editing a match" "description": "Button & Appbar label for editing a match"
}, },
"@enter_points": {
"description": "Label to enter players points"
},
"@enter_results": { "@enter_results": {
"description": "Button text to enter match results" "description": "Button text to enter match results"
}, },
@@ -232,6 +235,9 @@
} }
} }
}, },
"@points": {
"description": "Points label"
},
"@privacy_policy": { "@privacy_policy": {
"description": "Privacy policy menu item" "description": "Privacy policy menu item"
}, },
@@ -271,6 +277,9 @@
"@select_winner": { "@select_winner": {
"description": "Label to select the winner" "description": "Label to select the winner"
}, },
"@select_loser": {
"description": "Label to select the loser"
},
"@selected_players": { "@selected_players": {
"description": "Shows the number of selected players" "description": "Shows the number of selected players"
}, },
@@ -351,6 +360,7 @@
"delete_match": "Delete Match", "delete_match": "Delete Match",
"edit_group": "Edit Group", "edit_group": "Edit Group",
"edit_match": "Edit Match", "edit_match": "Edit Match",
"enter_points": "Enter points",
"enter_results": "Enter Results", "enter_results": "Enter Results",
"error_creating_group": "Error while creating group, please try again", "error_creating_group": "Error while creating group, please try again",
"error_deleting_group": "Error while deleting group, please try again", "error_deleting_group": "Error while deleting group, please try again",
@@ -399,6 +409,7 @@
"player_name": "Player name", "player_name": "Player name",
"players": "Players", "players": "Players",
"players_count": "{count} Players", "players_count": "{count} Players",
"points": "Points",
"privacy_policy": "Privacy Policy", "privacy_policy": "Privacy Policy",
"quick_create": "Quick Create", "quick_create": "Quick Create",
"recent_matches": "Recent Matches", "recent_matches": "Recent Matches",
@@ -411,12 +422,14 @@
"save_changes": "Save Changes", "save_changes": "Save Changes",
"search_for_groups": "Search for groups", "search_for_groups": "Search for groups",
"search_for_players": "Search for players", "search_for_players": "Search for players",
"select_winner": "Select Winner:", "select_winner": "Select Winner",
"select_loser": "Select Loser",
"selected_players": "Selected players", "selected_players": "Selected players",
"settings": "Settings", "settings": "Settings",
"single_loser": "Single Loser", "single_loser": "Single Loser",
"single_winner": "Single Winner", "single_winner": "Single Winner",
"highest_score": "Highest Score", "highest_score": "Highest Score",
"loser": "Loser",
"lowest_score": "Lowest Score", "lowest_score": "Lowest Score",
"multiple_winners": "Multiple Winners", "multiple_winners": "Multiple Winners",
"statistics": "Statistics", "statistics": "Statistics",

View File

@@ -254,6 +254,12 @@ abstract class AppLocalizations {
/// **'Edit Match'** /// **'Edit Match'**
String get edit_match; String get edit_match;
/// Label to enter players points
///
/// In en, this message translates to:
/// **'Enter points'**
String get enter_points;
/// Button text to enter match results /// Button text to enter match results
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -542,6 +548,12 @@ abstract class AppLocalizations {
/// **'{count} Players'** /// **'{count} Players'**
String players_count(int count); String players_count(int count);
/// Points label
///
/// In en, this message translates to:
/// **'Points'**
String get points;
/// Privacy policy menu item /// Privacy policy menu item
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -617,9 +629,15 @@ abstract class AppLocalizations {
/// Label to select the winner /// Label to select the winner
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Select Winner:'** /// **'Select Winner'**
String get select_winner; String get select_winner;
/// Label to select the loser
///
/// In en, this message translates to:
/// **'Select Loser'**
String get select_loser;
/// Shows the number of selected players /// Shows the number of selected players
/// ///
/// In en, this message translates to: /// In en, this message translates to:
@@ -650,6 +668,12 @@ abstract class AppLocalizations {
/// **'Highest Score'** /// **'Highest Score'**
String get highest_score; String get highest_score;
/// No description provided for @loser.
///
/// In en, this message translates to:
/// **'Loser'**
String get loser;
/// No description provided for @lowest_score. /// No description provided for @lowest_score.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -79,7 +79,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get delete_all_data => 'Alle Daten löschen'; String get delete_all_data => 'Alle Daten löschen';
@override @override
String get delete_group => 'Diese Gruppe löschen'; String get delete_group => 'Gruppe löschen';
@override @override
String get delete_match => 'Spiel löschen'; String get delete_match => 'Spiel löschen';
@@ -90,6 +90,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get edit_match => 'Gruppe bearbeiten'; String get edit_match => 'Gruppe bearbeiten';
@override
String get enter_points => 'Punkte eingeben';
@override @override
String get enter_results => 'Ergebnisse eintragen'; String get enter_results => 'Ergebnisse eintragen';
@@ -240,6 +243,9 @@ class AppLocalizationsDe extends AppLocalizations {
return '$count Spieler'; return '$count Spieler';
} }
@override
String get points => 'Punkte';
@override @override
String get privacy_policy => 'Datenschutzerklärung'; String get privacy_policy => 'Datenschutzerklärung';
@@ -281,7 +287,10 @@ class AppLocalizationsDe extends AppLocalizations {
String get search_for_players => 'Nach Spieler:innen suchen'; String get search_for_players => 'Nach Spieler:innen suchen';
@override @override
String get select_winner => 'Gewinner:in wählen:'; String get select_winner => 'Gewinner:in wählen';
@override
String get select_loser => 'Verlierer:in wählen';
@override @override
String get selected_players => 'Ausgewählte Spieler:innen'; String get selected_players => 'Ausgewählte Spieler:innen';
@@ -298,6 +307,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override @override
String get highest_score => 'Höchste Punkte'; String get highest_score => 'Höchste Punkte';
@override
String get loser => 'Verlierer:in';
@override @override
String get lowest_score => 'Niedrigste Punkte'; String get lowest_score => 'Niedrigste Punkte';

View File

@@ -90,6 +90,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get edit_match => 'Edit Match'; String get edit_match => 'Edit Match';
@override
String get enter_points => 'Enter points';
@override @override
String get enter_results => 'Enter Results'; String get enter_results => 'Enter Results';
@@ -240,6 +243,9 @@ class AppLocalizationsEn extends AppLocalizations {
return '$count Players'; return '$count Players';
} }
@override
String get points => 'Points';
@override @override
String get privacy_policy => 'Privacy Policy'; String get privacy_policy => 'Privacy Policy';
@@ -281,7 +287,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get search_for_players => 'Search for players'; String get search_for_players => 'Search for players';
@override @override
String get select_winner => 'Select Winner:'; String get select_winner => 'Select Winner';
@override
String get select_loser => 'Select Loser';
@override @override
String get selected_players => 'Selected players'; String get selected_players => 'Selected players';
@@ -298,6 +307,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get highest_score => 'Highest Score'; String get highest_score => 'Highest Score';
@override
String get loser => 'Loser';
@override @override
String get lowest_score => 'Lowest Score'; String get lowest_score => 'Lowest Score';

View File

@@ -227,7 +227,7 @@ class _HomeViewState extends State<HomeView> {
/// Updates the winner information for a specific match in the recent matches list. /// Updates the winner information for a specific match in the recent matches list.
Future<void> updatedWinnerInRecentMatches(String matchId) async { Future<void> updatedWinnerInRecentMatches(String matchId) async {
final db = Provider.of<AppDatabase>(context, listen: false); final db = Provider.of<AppDatabase>(context, listen: false);
final winner = await db.scoreDao.getWinner(matchId: matchId); final winner = await db.scoreEntryDao.getWinner(matchId: matchId);
final matchIndex = recentMatches.indexWhere((match) => match.id == matchId); final matchIndex = recentMatches.indexWhere((match) => match.id == matchId);
if (matchIndex != -1) { if (matchIndex != -1) {
setState(() { setState(() {

View File

@@ -4,6 +4,7 @@ import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart'; import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/common.dart'; import 'package:tallee/core/common.dart';
import 'package:tallee/core/custom_theme.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/db/database.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
@@ -175,37 +176,7 @@ class _MatchDetailViewState extends State<MatchDetailView> {
vertical: 4, vertical: 4,
horizontal: 8, horizontal: 8,
), ),
child: Row( child: getResultWidget(loc),
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
/// TODO: Implement different ruleset results display
if (match.winner != null) ...[
Text(
loc.winner,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
Text(
match.winner!.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CustomTheme.primaryColor,
),
),
] else ...[
Text(
loc.no_results_entered_yet,
style: const TextStyle(
fontSize: 14,
color: CustomTheme.textColor,
),
),
],
],
),
), ),
), ),
], ],
@@ -264,4 +235,91 @@ class _MatchDetailViewState extends State<MatchDetailView> {
}); });
widget.onMatchUpdate.call(); widget.onMatchUpdate.call();
} }
/// Returns the widget to be displayed in the result [InfoTile]
/// TODO: Update when score logic is overhauled
Widget getResultWidget(AppLocalizations loc) {
if (isSingleRowResult()) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: getResultRow(loc),
);
} else {
return Column(
children: [
for (var player in match.players)
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
player.name,
style: const TextStyle(
fontSize: 16,
color: CustomTheme.textColor,
),
),
Text(
'0 ${loc.points}',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CustomTheme.primaryColor,
),
),
],
),
],
);
}
}
/// Returns the result row for single winner/loser rulesets or a placeholder
/// if no result is entered yet
/// TODO: Update when score logic is overhauled
List<Widget> getResultRow(AppLocalizations loc) {
if (match.winner != null && match.game.ruleset == Ruleset.singleWinner) {
return [
Text(
loc.winner,
style: const TextStyle(fontSize: 16, color: CustomTheme.textColor),
),
Text(
match.winner!.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CustomTheme.primaryColor,
),
),
];
} else if (match.game.ruleset == Ruleset.singleLoser) {
return [
Text(
loc.loser,
style: const TextStyle(fontSize: 16, color: CustomTheme.textColor),
),
Text(
match.winner!.name,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: CustomTheme.primaryColor,
),
),
];
} else {
return [
Text(
loc.no_results_entered_yet,
style: const TextStyle(fontSize: 14, color: CustomTheme.textColor),
),
];
}
}
// Returns if the result can be displayed in a single row
bool isSingleRowResult() {
return match.game.ruleset == Ruleset.singleWinner ||
match.game.ruleset == Ruleset.singleLoser;
}
} }

View File

@@ -1,21 +1,33 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:tallee/core/custom_theme.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/db/database.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/l10n/generated/app_localizations.dart'; import 'package:tallee/l10n/generated/app_localizations.dart';
import 'package:tallee/presentation/widgets/buttons/custom_width_button.dart';
import 'package:tallee/presentation/widgets/tiles/custom_radio_list_tile.dart'; import 'package:tallee/presentation/widgets/tiles/custom_radio_list_tile.dart';
import 'package:tallee/presentation/widgets/tiles/score_list_tile.dart';
class MatchResultView extends StatefulWidget { class MatchResultView extends StatefulWidget {
/// A view that allows selecting and saving the winner of a match /// A view that allows selecting and saving the winner of a match
/// [match]: The match for which the winner is to be selected /// [match]: The match for which the winner is to be selected
/// [onWinnerChanged]: Optional callback invoked when the winner is changed /// [onWinnerChanged]: Optional callback invoked when the winner is changed
const MatchResultView({super.key, required this.match, this.onWinnerChanged}); const MatchResultView({
super.key,
required this.match,
this.ruleset = Ruleset.singleWinner,
this.onWinnerChanged,
});
/// The match for which the winner is to be selected /// The match for which the winner is to be selected
final Match match; final Match match;
/// The ruleset of the match, determines how the winner is selected or how
/// scores are entered
final Ruleset ruleset;
/// Optional callback invoked when the winner is changed /// Optional callback invoked when the winner is changed
final VoidCallback? onWinnerChanged; final VoidCallback? onWinnerChanged;
@@ -29,6 +41,9 @@ class _MatchResultViewState extends State<MatchResultView> {
/// List of all players who participated in the match /// List of all players who participated in the match
late final List<Player> allPlayers; late final List<Player> allPlayers;
/// List of text controllers for score entry, one for each player
late final List<TextEditingController> controller;
/// Currently selected winner player /// Currently selected winner player
Player? _selectedPlayer; Player? _selectedPlayer;
@@ -39,10 +54,19 @@ class _MatchResultViewState extends State<MatchResultView> {
allPlayers = widget.match.players; allPlayers = widget.match.players;
allPlayers.sort((a, b) => a.name.compareTo(b.name)); allPlayers.sort((a, b) => a.name.compareTo(b.name));
controller = List.generate(
allPlayers.length,
(index) => TextEditingController(),
);
if (widget.match.winner != null) { if (widget.match.winner != null) {
_selectedPlayer = allPlayers.firstWhere( if (rulesetSupportsWinnerSelection()) {
(p) => p.id == widget.match.winner!.id, _selectedPlayer = allPlayers.firstWhere(
); (p) => p.id == widget.match.winner!.id,
);
} else if (rulesetSupportsScoreEntry()) {
/// TODO: Update when score logic is overhauled
}
} }
super.initState(); super.initState();
} }
@@ -50,6 +74,7 @@ class _MatchResultViewState extends State<MatchResultView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
return Scaffold( return Scaffold(
backgroundColor: CustomTheme.backgroundColor, backgroundColor: CustomTheme.backgroundColor,
appBar: AppBar( appBar: AppBar(
@@ -85,50 +110,77 @@ class _MatchResultViewState extends State<MatchResultView> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
loc.select_winner, '${getTitleForRuleset(loc)}:',
style: const TextStyle( style: const TextStyle(
fontSize: 18, fontSize: 18,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Expanded( if (rulesetSupportsWinnerSelection())
child: RadioGroup<Player>( Expanded(
groupValue: _selectedPlayer, child: RadioGroup<Player>(
onChanged: (Player? value) async { groupValue: _selectedPlayer,
setState(() { onChanged: (Player? value) async {
_selectedPlayer = value; setState(() {
}); _selectedPlayer = value;
await _handleWinnerSaving(); });
}, },
child: ListView.builder( child: ListView.builder(
itemCount: allPlayers.length,
itemBuilder: (context, index) {
return CustomRadioListTile(
text: allPlayers[index].name,
value: allPlayers[index],
onContainerTap: (value) async {
setState(() {
// Check if the already selected player is the same as the newly tapped player.
if (_selectedPlayer == value) {
// If yes deselected the player by setting it to null.
_selectedPlayer = null;
} else {
// If no assign the newly tapped player to the selected player.
(_selectedPlayer = value);
}
});
},
);
},
),
),
),
if (rulesetSupportsScoreEntry())
Expanded(
child: ListView.separated(
itemCount: allPlayers.length, itemCount: allPlayers.length,
itemBuilder: (context, index) { itemBuilder: (context, index) {
return CustomRadioListTile( print(allPlayers[index].name);
return ScoreListTile(
text: allPlayers[index].name, text: allPlayers[index].name,
value: allPlayers[index], controller: controller[index],
onContainerTap: (value) async { );
setState(() { },
// Check if the already selected player is the same as the newly tapped player. separatorBuilder: (BuildContext context, int index) {
if (_selectedPlayer == value) { return const Padding(
// If yes deselected the player by setting it to null. padding: EdgeInsets.symmetric(vertical: 8.0),
_selectedPlayer = null; child: Divider(indent: 20),
} else {
// If no assign the newly tapped player to the selected player.
(_selectedPlayer = value);
}
});
await _handleWinnerSaving();
},
); );
}, },
), ),
), ),
),
], ],
), ),
), ),
), ),
CustomWidthButton(
text: loc.save_changes,
sizeRelativeToWidth: 0.95,
onPressed: () async {
await _handleSaving();
if (!context.mounted) return;
Navigator.of(context).pop(_selectedPlayer);
},
),
], ],
), ),
), ),
@@ -137,15 +189,75 @@ class _MatchResultViewState extends State<MatchResultView> {
/// Handles saving or removing the winner in the database /// Handles saving or removing the winner in the database
/// based on the current selection. /// based on the current selection.
Future<void> _handleWinnerSaving() async { Future<void> _handleSaving() async {
if (widget.ruleset == Ruleset.singleWinner) {
await _handleWinner();
} else if (widget.ruleset == Ruleset.singleLoser) {
await _handleLoser();
} else if (widget.ruleset == Ruleset.lowestScore ||
widget.ruleset == Ruleset.highestScore) {
await _handleScores();
}
widget.onWinnerChanged?.call();
}
Future<bool> _handleWinner() async {
if (_selectedPlayer == null) { if (_selectedPlayer == null) {
await db.scoreDao.removeWinner(matchId: widget.match.id); await db.scoreEntryDao.removeWinner(matchId: widget.match.id);
} else { } else {
await db.scoreDao.setWinner( await db.scoreEntryDao.setWinner(
matchId: widget.match.id, matchId: widget.match.id,
playerId: _selectedPlayer!.id, playerId: _selectedPlayer!.id,
); );
} }
widget.onWinnerChanged?.call(); }
Future<bool> _handleLoser() async {
if (_selectedPlayer == null) {
/// TODO: Update when score logic is overhauled
return false;
} else {
/// TODO: Update when score logic is overhauled
return false;
}
}
/// Handles saving the scores for each player in the database.
Future<bool> _handleScores() async {
for (int i = 0; i < allPlayers.length; i++) {
var text = controller[i].text;
if (text.isEmpty) {
text = '0';
}
final score = int.parse(text);
await db.playerMatchDao.updatePlayerScore(
matchId: widget.match.id,
playerId: allPlayers[i].id,
newScore: score,
);
}
return false;
}
String getTitleForRuleset(AppLocalizations loc) {
switch (widget.ruleset) {
case Ruleset.singleWinner:
return loc.select_winner;
case Ruleset.singleLoser:
return loc.select_loser;
default:
return loc.enter_points;
}
}
bool rulesetSupportsWinnerSelection() {
return widget.ruleset == Ruleset.singleWinner ||
widget.ruleset == Ruleset.singleLoser;
}
bool rulesetSupportsScoreEntry() {
return widget.ruleset == Ruleset.lowestScore ||
widget.ruleset == Ruleset.highestScore;
} }
} }

View File

@@ -70,6 +70,8 @@ const allDependencies = <Package>[
_http_parser, _http_parser,
_intl, _intl,
_io, _io,
_jni,
_jni_flutter,
_js, _js,
_json_annotation, _json_annotation,
_json_schema, _json_schema,
@@ -360,13 +362,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// async 2.13.0 /// async 2.13.1
const _async = Package( const _async = Package(
name: 'async', name: 'async',
description: "Utility functions and classes related to the 'dart:async' library.", description: "Utility functions and classes related to the 'dart:async' library.",
repository: 'https://github.com/dart-lang/core/tree/main/pkgs/async', repository: 'https://github.com/dart-lang/core/tree/main/pkgs/async',
authors: [], authors: [],
version: '2.13.0', version: '2.13.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -442,13 +444,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// build 4.0.4 /// build 4.0.5
const _build = Package( const _build = Package(
name: 'build', name: 'build',
description: 'A package for authoring build_runner compatible code generators.', description: 'A package for authoring build_runner compatible code generators.',
repository: 'https://github.com/dart-lang/build/tree/master/build', repository: 'https://github.com/dart-lang/build/tree/master/build',
authors: [], authors: [],
version: '4.0.4', version: '4.0.5',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -565,13 +567,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// build_runner 2.12.2 /// build_runner 2.13.1
const _build_runner = Package( const _build_runner = Package(
name: 'build_runner', name: 'build_runner',
description: 'A build system for Dart code generation and modular compilation.', description: 'A build system for Dart code generation and modular compilation.',
repository: 'https://github.com/dart-lang/build/tree/master/build_runner', repository: 'https://github.com/dart-lang/build/tree/master/build_runner',
authors: [], authors: [],
version: '2.12.2', version: '2.13.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -649,14 +651,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// built_value 8.12.4 /// built_value 8.12.5
const _built_value = Package( const _built_value = Package(
name: 'built_value', name: 'built_value',
description: '''Value types with builders, Dart classes as enums, and serialization. This library is the runtime dependency. description: '''Value types with builders, Dart classes as enums, and serialization. This library is the runtime dependency.
''', ''',
repository: 'https://github.com/google/built_value.dart/tree/master/built_value', repository: 'https://github.com/google/built_value.dart/tree/master/built_value',
authors: [], authors: [],
version: '8.12.4', version: '8.12.5',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -1436,18 +1438,18 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// cupertino_icons 1.0.8 /// cupertino_icons 1.0.9
const _cupertino_icons = Package( const _cupertino_icons = Package(
name: 'cupertino_icons', name: 'cupertino_icons',
description: 'Default icons asset for Cupertino widgets based on Apple styled icons', description: 'Default icons asset for Cupertino widgets based on Apple styled icons',
repository: 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons', repository: 'https://github.com/flutter/packages/tree/main/third_party/packages/cupertino_icons',
authors: [], authors: [],
version: '1.0.8', version: '1.0.9',
spdxIdentifiers: ['MIT'], spdxIdentifiers: ['MIT'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [], dependencies: [],
devDependencies: [PackageRef('flutter'), PackageRef('flutter_test')], devDependencies: [PackageRef('collection'), PackageRef('flutter'), PackageRef('flutter_test'), PackageRef('path')],
license: '''The MIT License (MIT) license: '''The MIT License (MIT)
Copyright (c) 2016 Vladimir Kharlampidi Copyright (c) 2016 Vladimir Kharlampidi
@@ -2627,13 +2629,13 @@ const _flutter_localizations = Package(
devDependencies: [PackageRef('flutter_test')], devDependencies: [PackageRef('flutter_test')],
); );
/// flutter_plugin_android_lifecycle 2.0.33 /// flutter_plugin_android_lifecycle 2.0.34
const _flutter_plugin_android_lifecycle = Package( const _flutter_plugin_android_lifecycle = Package(
name: 'flutter_plugin_android_lifecycle', name: 'flutter_plugin_android_lifecycle',
description: 'Flutter plugin for accessing an Android Lifecycle within other plugins.', description: 'Flutter plugin for accessing an Android Lifecycle within other plugins.',
repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_plugin_android_lifecycle', repository: 'https://github.com/flutter/packages/tree/main/packages/flutter_plugin_android_lifecycle',
authors: [], authors: [],
version: '2.0.33', version: '2.0.34',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -3173,6 +3175,88 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// jni 1.0.0
const _jni = Package(
name: 'jni',
description: 'A library to access JNI from Dart and Flutter that acts as a support library for package:jnigen.',
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/jni',
authors: [],
version: '1.0.0',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
dependencies: [PackageRef('args'), PackageRef('collection'), PackageRef('ffi'), PackageRef('meta'), PackageRef('package_config'), PackageRef('path'), PackageRef('plugin_platform_interface')],
devDependencies: [PackageRef('dart_style'), PackageRef('logging'), PackageRef('test')],
license: '''Copyright 2022, 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.''',
);
/// jni_flutter 1.0.1
const _jni_flutter = Package(
name: 'jni_flutter',
description: 'A library to access Flutter Android specific APIs from Dart.',
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/jni_flutter',
authors: [],
version: '1.0.1',
spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false,
isSdk: false,
dependencies: [PackageRef('flutter'), PackageRef('jni')],
devDependencies: [PackageRef('flutter_test')],
license: '''Copyright 2026, 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.''',
);
/// js 0.7.2 /// js 0.7.2
const _js = Package( const _js = Package(
name: 'js', name: 'js',
@@ -3511,13 +3595,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// markdown 7.3.0 /// markdown 7.3.1
const _markdown = Package( const _markdown = Package(
name: 'markdown', name: 'markdown',
description: 'A portable Markdown library written in Dart that can parse Markdown into HTML.', description: 'A portable Markdown library written in Dart that can parse Markdown into HTML.',
repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/markdown', repository: 'https://github.com/dart-lang/tools/tree/main/pkgs/markdown',
authors: [], authors: [],
version: '7.3.0', version: '7.3.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -3890,13 +3974,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// native_toolchain_c 0.17.5 /// native_toolchain_c 0.17.6
const _native_toolchain_c = Package( const _native_toolchain_c = Package(
name: 'native_toolchain_c', name: 'native_toolchain_c',
description: 'A library to invoke the native C compiler installed on the host machine.', description: 'A library to invoke the native C compiler installed on the host machine.',
repository: 'https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c', repository: 'https://github.com/dart-lang/native/tree/main/pkgs/native_toolchain_c',
authors: [], authors: [],
version: '0.17.5', version: '0.17.6',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -4110,14 +4194,14 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// package_info_plus 9.0.0 /// package_info_plus 9.0.1
const _package_info_plus = Package( const _package_info_plus = Package(
name: 'package_info_plus', name: 'package_info_plus',
description: 'Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.', description: 'Flutter plugin for querying information about the application package, such as CFBundleVersion on iOS or versionCode on Android.',
homepage: 'https://github.com/fluttercommunity/plus_plugins', homepage: 'https://github.com/fluttercommunity/plus_plugins',
repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus', repository: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/package_info_plus/package_info_plus',
authors: [], authors: [],
version: '9.0.0', version: '9.0.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -4194,13 +4278,13 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// pana 0.23.10 /// pana 0.23.11
const _pana = Package( const _pana = Package(
name: 'pana', name: 'pana',
description: 'PAckage aNAlyzer - produce a report summarizing the health and quality of a Dart package.', description: 'PAckage aNAlyzer - produce a report summarizing the health and quality of a Dart package.',
repository: 'https://github.com/dart-lang/pana', repository: 'https://github.com/dart-lang/pana',
authors: [], authors: [],
version: '0.23.10', version: '0.23.11',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -4315,17 +4399,17 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// path_provider_android 2.2.22 /// path_provider_android 2.3.1
const _path_provider_android = Package( const _path_provider_android = Package(
name: 'path_provider_android', name: 'path_provider_android',
description: 'Android implementation of the path_provider plugin.', description: 'Android implementation of the path_provider plugin.',
repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android', repository: 'https://github.com/flutter/packages/tree/main/packages/path_provider/path_provider_android',
authors: [], authors: [],
version: '2.2.22', version: '2.3.1',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
dependencies: [PackageRef('flutter'), PackageRef('path_provider_platform_interface')], dependencies: [PackageRef('flutter'), PackageRef('jni'), PackageRef('jni_flutter'), PackageRef('path_provider_platform_interface')],
devDependencies: [PackageRef('flutter_test'), PackageRef('test')], devDependencies: [PackageRef('flutter_test'), PackageRef('test')],
license: '''Copyright 2013 The Flutter Authors license: '''Copyright 2013 The Flutter Authors
@@ -36119,13 +36203,13 @@ Copyright (C) 2009-2017, International Business Machines Corporation,
Google, and others. All Rights Reserved.''', Google, and others. All Rights Reserved.''',
); );
/// source_gen 4.2.0 /// source_gen 4.2.2
const _source_gen = Package( const _source_gen = Package(
name: 'source_gen', name: 'source_gen',
description: 'Source code generation builders and utilities for the Dart build system', description: 'Source code generation builders and utilities for the Dart build system',
repository: 'https://github.com/dart-lang/source_gen/tree/master/source_gen', repository: 'https://github.com/dart-lang/source_gen/tree/master/source_gen',
authors: [], authors: [],
version: '4.2.0', version: '4.2.2',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -36835,13 +36919,13 @@ ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''', SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.''',
); );
/// url_launcher_android 6.3.28 /// url_launcher_android 6.3.29
const _url_launcher_android = Package( const _url_launcher_android = Package(
name: 'url_launcher_android', name: 'url_launcher_android',
description: 'Android implementation of the url_launcher plugin.', description: 'Android implementation of the url_launcher plugin.',
repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android', repository: 'https://github.com/flutter/packages/tree/main/packages/url_launcher/url_launcher_android',
authors: [], authors: [],
version: '6.3.28', version: '6.3.29',
spdxIdentifiers: ['BSD-3-Clause'], spdxIdentifiers: ['BSD-3-Clause'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,
@@ -37796,12 +37880,12 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.''', SOFTWARE.''',
); );
/// tallee 0.0.19+253 /// tallee 0.0.20+254
const _tallee = Package( const _tallee = Package(
name: 'tallee', name: 'tallee',
description: 'Tracking App for Card Games', description: 'Tracking App for Card Games',
authors: [], authors: [],
version: '0.0.19+253', version: '0.0.20+254',
spdxIdentifiers: ['LGPL-3.0'], spdxIdentifiers: ['LGPL-3.0'],
isMarkdown: false, isMarkdown: false,
isSdk: false, isSdk: false,

View File

@@ -79,6 +79,24 @@ class _MatchTileState extends State<MatchTile> {
], ],
), ),
const SizedBox(height: 4),
Container(
decoration: BoxDecoration(
color: CustomTheme.primaryColor,
borderRadius: BorderRadius.circular(8),
),
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
child: Text(
translateRulesetToString(match.game.ruleset, context),
style: const TextStyle(
fontSize: 12,
color: Colors.white,
fontWeight: FontWeight.bold,
),
),
),
const SizedBox(height: 8), const SizedBox(height: 8),
if (group != null) ...[ if (group != null) ...[

View File

@@ -0,0 +1,91 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:tallee/core/custom_theme.dart';
import 'package:tallee/l10n/generated/app_localizations.dart';
class ScoreListTile extends StatelessWidget {
/// A custom list tile widget that has a text field for inputting a score.
/// - [text]: The leading text to be displayed.
/// - [controller]: The controller for the text field to input the score.
const ScoreListTile({
super.key,
required this.text,
required this.controller,
/*
required this.onContainerTap,
*/
});
/// The text to display next to the radio button.
final String text;
final TextEditingController controller;
/// The callback invoked when the container is tapped.
/*
final ValueChanged<T> onContainerTap;
*/
@override
Widget build(BuildContext context) {
final loc = AppLocalizations.of(context);
return Container(
margin: const EdgeInsets.symmetric(horizontal: 5, vertical: 5),
padding: const EdgeInsets.symmetric(horizontal: 20),
decoration: const BoxDecoration(color: CustomTheme.boxColor),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
text,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w500),
),
SizedBox(
width: 100,
height: 40,
child: TextField(
controller: controller,
keyboardType: TextInputType.number,
maxLength: 4,
inputFormatters: [FilteringTextInputFormatter.digitsOnly],
textAlign: TextAlign.center,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: CustomTheme.textColor,
),
cursorColor: CustomTheme.textColor,
decoration: InputDecoration(
hintText: loc.points,
counterText: '',
filled: true,
fillColor: CustomTheme.onBoxColor,
contentPadding: const EdgeInsets.symmetric(
horizontal: 0,
vertical: 0,
),
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: BorderSide(
color: CustomTheme.textColor.withAlpha(100),
width: 2,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(8),
borderSide: const BorderSide(
color: CustomTheme.primaryColor,
width: 2,
),
),
),
),
),
],
),
);
}
}

View File

@@ -18,6 +18,7 @@ class DataTransferService {
/// Deletes all data from the database. /// Deletes all data from the database.
static Future<void> deleteAllData(BuildContext context) async { static Future<void> deleteAllData(BuildContext context) async {
final db = Provider.of<AppDatabase>(context, listen: false); final db = Provider.of<AppDatabase>(context, listen: false);
await db.matchDao.deleteAllMatches(); await db.matchDao.deleteAllMatches();
await db.teamDao.deleteAllTeams(); await db.teamDao.deleteAllTeams();
await db.groupDao.deleteAllGroups(); await db.groupDao.deleteAllGroups();
@@ -96,8 +97,8 @@ class DataTransferService {
/// Exports the given JSON string to a file with the specified name. /// Exports the given JSON string to a file with the specified name.
/// Returns an [ExportResult] indicating the outcome. /// Returns an [ExportResult] indicating the outcome.
/// ///
/// [jsonString] The JSON string to be exported. /// - [jsonString]: The JSON string to be exported.
/// [fileName] The desired name for the exported file (without extension). /// - [fileName]: The desired name for the exported file (without extension).
static Future<ExportResult> exportData( static Future<ExportResult> exportData(
String jsonString, String jsonString,
String fileName, String fileName,
@@ -163,21 +164,22 @@ class DataTransferService {
@visibleForTesting @visibleForTesting
static Future<void> importDataToDatabase( static Future<void> importDataToDatabase(
AppDatabase db, AppDatabase db,
Map<String, dynamic> decoded, Map<String, dynamic> decodedJson,
) async { ) async {
final importedPlayers = parsePlayersFromJson(decoded); // Fetch all entities first to create lookup maps for relationships
final importedPlayers = parsePlayersFromJson(decodedJson);
final playerById = {for (final p in importedPlayers) p.id: p}; final playerById = {for (final p in importedPlayers) p.id: p};
final importedGames = parseGamesFromJson(decoded); final importedGames = parseGamesFromJson(decodedJson);
final gameById = {for (final g in importedGames) g.id: g}; final gameById = {for (final g in importedGames) g.id: g};
final importedGroups = parseGroupsFromJson(decoded, playerById); final importedGroups = parseGroupsFromJson(decodedJson, playerById);
final groupById = {for (final g in importedGroups) g.id: g}; final groupById = {for (final g in importedGroups) g.id: g};
final importedTeams = parseTeamsFromJson(decoded, playerById); final importedTeams = parseTeamsFromJson(decodedJson, playerById);
final importedMatches = parseMatchesFromJson( final importedMatches = parseMatchesFromJson(
decoded, decodedJson,
gameById, gameById,
groupById, groupById,
playerById, playerById,
@@ -190,31 +192,30 @@ class DataTransferService {
await db.matchDao.addMatchAsList(matches: importedMatches); await db.matchDao.addMatchAsList(matches: importedMatches);
} }
/// Parses players from JSON data. /* Parsing Methods */
@visibleForTesting @visibleForTesting
static List<Player> parsePlayersFromJson(Map<String, dynamic> decoded) { static List<Player> parsePlayersFromJson(Map<String, dynamic> decodedJson) {
final playersJson = (decoded['players'] as List<dynamic>?) ?? []; final playersJson = (decodedJson['players'] as List<dynamic>?) ?? [];
return playersJson return playersJson
.map((p) => Player.fromJson(p as Map<String, dynamic>)) .map((p) => Player.fromJson(p as Map<String, dynamic>))
.toList(); .toList();
} }
/// Parses games from JSON data.
@visibleForTesting @visibleForTesting
static List<Game> parseGamesFromJson(Map<String, dynamic> decoded) { static List<Game> parseGamesFromJson(Map<String, dynamic> decodedJson) {
final gamesJson = (decoded['games'] as List<dynamic>?) ?? []; final gamesJson = (decodedJson['games'] as List<dynamic>?) ?? [];
return gamesJson return gamesJson
.map((g) => Game.fromJson(g as Map<String, dynamic>)) .map((g) => Game.fromJson(g as Map<String, dynamic>))
.toList(); .toList();
} }
/// Parses groups from JSON data.
@visibleForTesting @visibleForTesting
static List<Group> parseGroupsFromJson( static List<Group> parseGroupsFromJson(
Map<String, dynamic> decoded, Map<String, dynamic> decodedJson,
Map<String, Player> playerById, Map<String, Player> playerById,
) { ) {
final groupsJson = (decoded['groups'] as List<dynamic>?) ?? []; final groupsJson = (decodedJson['groups'] as List<dynamic>?) ?? [];
return groupsJson.map((g) { return groupsJson.map((g) {
final map = g as Map<String, dynamic>; final map = g as Map<String, dynamic>;
final memberIds = (map['memberIds'] as List<dynamic>? ?? []) final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
@@ -238,10 +239,10 @@ class DataTransferService {
/// Parses teams from JSON data. /// Parses teams from JSON data.
@visibleForTesting @visibleForTesting
static List<Team> parseTeamsFromJson( static List<Team> parseTeamsFromJson(
Map<String, dynamic> decoded, Map<String, dynamic> decodedJson,
Map<String, Player> playerById, Map<String, Player> playerById,
) { ) {
final teamsJson = (decoded['teams'] as List<dynamic>?) ?? []; final teamsJson = (decodedJson['teams'] as List<dynamic>?) ?? [];
return teamsJson.map((t) { return teamsJson.map((t) {
final map = t as Map<String, dynamic>; final map = t as Map<String, dynamic>;
final memberIds = (map['memberIds'] as List<dynamic>? ?? []) final memberIds = (map['memberIds'] as List<dynamic>? ?? [])
@@ -264,46 +265,53 @@ class DataTransferService {
/// Parses matches from JSON data. /// Parses matches from JSON data.
@visibleForTesting @visibleForTesting
static List<Match> parseMatchesFromJson( static List<Match> parseMatchesFromJson(
Map<String, dynamic> decoded, Map<String, dynamic> decodedJson,
Map<String, Game> gameById, Map<String, Game> gamesMap,
Map<String, Group> groupById, Map<String, Group> groupsMap,
Map<String, Player> playerById, Map<String, Player> playersMap,
) { ) {
final matchesJson = (decoded['matches'] as List<dynamic>?) ?? []; final matchesJson = (decodedJson['matches'] as List<dynamic>?) ?? [];
return matchesJson.map((m) { return matchesJson.map((m) {
final map = m as Map<String, dynamic>; final map = m as Map<String, dynamic>;
// Extract attributes from json
final id = map['id'] as String;
final name = map['name'] as String;
final gameId = map['gameId'] as String; final gameId = map['gameId'] as String;
final groupId = map['groupId'] as String?; final groupId = map['groupId'] as String?;
final playerIds = (map['playerIds'] as List<dynamic>? ?? []) final createdAt = DateTime.parse(map['createdAt'] as String);
.cast<String>();
final endedAt = map['endedAt'] != null final endedAt = map['endedAt'] != null
? DateTime.parse(map['endedAt'] as String) ? DateTime.parse(map['endedAt'] as String)
: null; : null;
final notes = map['notes'] as String? ?? '';
final game = gameById[gameId] ?? createUnknownGame(); // Link attributes to objects
final group = groupId != null ? groupById[groupId] : null; final game = gamesMap[gameId] ?? getFallbackGame();
final group = groupId != null ? groupsMap[groupId] : null;
final playerIds = (map['playerIds'] as List<dynamic>? ?? [])
.cast<String>();
final players = playerIds final players = playerIds
.map((id) => playerById[id]) .map((id) => playersMap[id])
.whereType<Player>() .whereType<Player>()
.toList(); .toList();
return Match( return Match(
id: map['id'] as String, id: id,
name: map['name'] as String, name: name,
game: game, game: game,
group: group, group: group,
players: players, players: players,
createdAt: DateTime.parse(map['createdAt'] as String), createdAt: createdAt,
endedAt: endedAt, endedAt: endedAt,
notes: map['notes'] as String? ?? '', notes: notes,
); );
}).toList(); }).toList();
} }
/// Creates a fallback game when the referenced game is not found. /// Creates a fallback game when the referenced game is not found.
@visibleForTesting @visibleForTesting
static Game createUnknownGame() { static Game getFallbackGame() {
return Game( return Game(
name: 'Unknown', name: 'Unknown',
ruleset: Ruleset.singleWinner, ruleset: Ruleset.singleWinner,
@@ -320,7 +328,8 @@ class DataTransferService {
return null; return null;
} }
/// Validates the given JSON string against the predefined schema. /// Validates the given JSON string against the schema
/// in `assets/schema.json`.
@visibleForTesting @visibleForTesting
static Future<bool> validateJsonSchema(String jsonString) async { static Future<bool> validateJsonSchema(String jsonString) async {
final String schemaString; final String schemaString;

View File

@@ -1,7 +1,7 @@
name: tallee name: tallee
description: "Tracking App for Card Games" description: "Tracking App for Card Games"
publish_to: 'none' publish_to: 'none'
version: 0.0.19+253 version: 0.0.20+254
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1

View File

@@ -296,7 +296,7 @@ void main() {
test('Setting a winner works correctly', () async { test('Setting a winner works correctly', () async {
await database.matchDao.addMatch(match: testMatch1); await database.matchDao.addMatch(match: testMatch1);
await database.scoreDao.setWinner( await database.scoreEntryDao.setWinner(
matchId: testMatch1.id, matchId: testMatch1.id,
playerId: testPlayer5.id, playerId: testPlayer5.id,
); );

View File

@@ -267,21 +267,6 @@ void main() {
expect(players, isNull); expect(players, isNull);
}); });
test(
'updatePlayerScore returns false for non-existent player-match',
() async {
await database.matchDao.addMatch(match: testMatchOnlyGroup);
final updated = await database.scoreDao.updateScore(
matchId: testMatchOnlyGroup.id,
playerId: 'non-existent-player-id',
newScore: 50,
);
expect(updated, false);
},
);
test('Adding player with teamId works correctly', () async { test('Adding player with teamId works correctly', () async {
await database.matchDao.addMatch(match: testMatchOnlyGroup); await database.matchDao.addMatch(match: testMatchOnlyGroup);
await database.teamDao.addTeam(team: testTeam1); await database.teamDao.addTeam(team: testTeam1);

View File

@@ -7,7 +7,7 @@ import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/models/game.dart'; import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score.dart'; import 'package:tallee/data/models/score_entry.dart';
void main() { void main() {
late AppDatabase database; late AppDatabase database;
@@ -69,15 +69,14 @@ void main() {
group('Score Tests', () { group('Score Tests', () {
group('Adding and Fetching scores', () { group('Adding and Fetching scores', () {
test('Single Score', () async { test('Single Score', () async {
await database.scoreDao.addScore( ScoreEntry entry = ScoreEntry(roundNumber: 1, score: 10, change: 10);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry,
score: 10,
change: 10,
); );
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, roundNumber: 1,
@@ -91,18 +90,18 @@ void main() {
test('Multiple Scores', () async { test('Multiple Scores', () async {
final entryList = [ final entryList = [
Score(roundNumber: 1, score: 5, change: 5), ScoreEntry(roundNumber: 1, score: 5, change: 5),
Score(roundNumber: 2, score: 12, change: 7), ScoreEntry(roundNumber: 2, score: 12, change: 7),
Score(roundNumber: 3, score: 18, change: 6), ScoreEntry(roundNumber: 3, score: 18, change: 6),
]; ];
await database.scoreDao.addScoresAsList( await database.scoreEntryDao.addScoresAsList(
scores: entryList, entrys: entryList,
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
final scores = await database.scoreDao.getAllPlayerScoresInMatch( final scores = await database.scoreEntryDao.getAllPlayerScoresInMatch(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -120,15 +119,14 @@ void main() {
group('Undesirable values', () { group('Undesirable values', () {
test('Score & Round can have negative values', () async { test('Score & Round can have negative values', () async {
await database.scoreDao.addScore( ScoreEntry entry = ScoreEntry(roundNumber: -2, score: -10, change: -10);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: -2, entry: entry,
score: -10,
change: -10,
); );
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: -2, roundNumber: -2,
@@ -141,15 +139,14 @@ void main() {
}); });
test('Score & Round can have zero values', () async { test('Score & Round can have zero values', () async {
await database.scoreDao.addScore( ScoreEntry entry = ScoreEntry(roundNumber: 0, score: 0, change: 0);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 0, entry: entry,
score: 0,
change: 0,
); );
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 0, roundNumber: 0,
@@ -161,7 +158,7 @@ void main() {
}); });
test('Getting score for a non-existent entities returns null', () async { test('Getting score for a non-existent entities returns null', () async {
var score = await database.scoreDao.getScore( var score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: -1, roundNumber: -1,
@@ -169,14 +166,14 @@ void main() {
expect(score, isNull); expect(score, isNull);
score = await database.scoreDao.getScore( score = await database.scoreEntryDao.getScore(
playerId: 'non-existin-player', playerId: 'non-existin-player',
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(score, isNull); expect(score, isNull);
score = await database.scoreDao.getScore( score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: 'non-existing-match', matchId: 'non-existing-match',
); );
@@ -185,23 +182,20 @@ void main() {
}); });
test('Getting score for a non-match player returns null', () async { test('Getting score for a non-match player returns null', () async {
await database.scoreDao.addScore( ScoreEntry entry = ScoreEntry(roundNumber: 1, score: 10, change: 10);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry,
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer3.id, playerId: testPlayer3.id,
matchId: testMatch2.id, matchId: testMatch2.id,
roundNumber: 1, entry: entry,
score: 10,
change: 10,
); );
var score = await database.scoreDao.getScore( var score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch2.id, matchId: testMatch2.id,
roundNumber: 1, roundNumber: 1,
@@ -213,29 +207,26 @@ void main() {
group('Scores in matches', () { group('Scores in matches', () {
test('getAllMatchScores()', () async { test('getAllMatchScores()', () async {
await database.scoreDao.addScore( ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 1, score: 20, change: 20);
ScoreEntry entry3 = ScoreEntry(roundNumber: 2, score: 25, change: 15);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry1,
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer2.id, playerId: testPlayer2.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry2,
score: 20,
change: 20,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, entry: entry3,
score: 25,
change: 15,
); );
final scores = await database.scoreDao.getAllMatchScores( final scores = await database.scoreEntryDao.getAllMatchScores(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -245,7 +236,7 @@ void main() {
}); });
test('getAllMatchScores() with no scores saved', () async { test('getAllMatchScores() with no scores saved', () async {
final scores = await database.scoreDao.getAllMatchScores( final scores = await database.scoreEntryDao.getAllMatchScores(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -253,32 +244,25 @@ void main() {
}); });
test('getAllPlayerScoresInMatch()', () async { test('getAllPlayerScoresInMatch()', () async {
await database.scoreDao.addScore( ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 2, score: 25, change: 15);
ScoreEntry entry3 = ScoreEntry(roundNumber: 1, score: 30, change: 30);
await database.scoreEntryDao.addScoresAsList(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entrys: [entry1, entry2],
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id,
matchId: testMatch1.id,
roundNumber: 2,
score: 25,
change: 15,
);
await database.scoreDao.addScore(
playerId: testPlayer2.id, playerId: testPlayer2.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry3,
score: 30,
change: 30,
); );
final playerScores = await database.scoreDao.getAllPlayerScoresInMatch( final playerScores = await database.scoreEntryDao
playerId: testPlayer1.id, .getAllPlayerScoresInMatch(
matchId: testMatch1.id, playerId: testPlayer1.id,
); matchId: testMatch1.id,
);
expect(playerScores.length, 2); expect(playerScores.length, 2);
expect(playerScores[0].roundNumber, 1); expect(playerScores[0].roundNumber, 1);
@@ -290,43 +274,44 @@ void main() {
}); });
test('getAllPlayerScoresInMatch() with no scores saved', () async { test('getAllPlayerScoresInMatch() with no scores saved', () async {
final playerScores = await database.scoreDao.getAllPlayerScoresInMatch( final playerScores = await database.scoreEntryDao
playerId: testPlayer1.id, .getAllPlayerScoresInMatch(
matchId: testMatch1.id, playerId: testPlayer1.id,
); matchId: testMatch1.id,
);
expect(playerScores.isEmpty, true); expect(playerScores.isEmpty, true);
}); });
test('Scores are isolated across different matches', () async { test('Scores are isolated across different matches', () async {
await database.scoreDao.addScore( ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 1, score: 50, change: 50);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry1,
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch2.id, matchId: testMatch2.id,
roundNumber: 1, entry: entry2,
score: 50,
change: 50,
); );
final match1Scores = await database.scoreDao.getAllPlayerScoresInMatch( final match1Scores = await database.scoreEntryDao
playerId: testPlayer1.id, .getAllPlayerScoresInMatch(
matchId: testMatch1.id, playerId: testPlayer1.id,
); matchId: testMatch1.id,
);
expect(match1Scores.length, 1); expect(match1Scores.length, 1);
expect(match1Scores[0].score, 10); expect(match1Scores[0].score, 10);
expect(match1Scores[0].change, 10); expect(match1Scores[0].change, 10);
final match2Scores = await database.scoreDao.getAllPlayerScoresInMatch( final match2Scores = await database.scoreEntryDao
playerId: testPlayer1.id, .getAllPlayerScoresInMatch(
matchId: testMatch2.id, playerId: testPlayer1.id,
); matchId: testMatch2.id,
);
expect(match2Scores.length, 1); expect(match2Scores.length, 1);
expect(match2Scores[0].score, 50); expect(match2Scores[0].score, 50);
@@ -336,33 +321,29 @@ void main() {
group('Updating scores', () { group('Updating scores', () {
test('updateScore()', () async { test('updateScore()', () async {
await database.scoreDao.addScore( ScoreEntry entry1 = ScoreEntry(roundNumber: 1, score: 10, change: 10);
ScoreEntry entry2 = ScoreEntry(roundNumber: 2, score: 15, change: 5);
await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: entry1,
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, entry: entry2,
score: 15,
change: 5,
); );
final updated = await database.scoreDao.updateScore( final updated = await database.scoreEntryDao.updateScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, newEntry: ScoreEntry(roundNumber: 2, score: 50, change: 40),
newScore: 50,
newChange: 40,
); );
expect(updated, true); expect(updated, true);
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, roundNumber: 2,
@@ -374,12 +355,10 @@ void main() {
}); });
test('Updating a non-existent score returns false', () async { test('Updating a non-existent score returns false', () async {
final updated = await database.scoreDao.updateScore( final updated = await database.scoreEntryDao.updateScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, newEntry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
newScore: 20,
newChange: 20,
); );
expect(updated, false); expect(updated, false);
@@ -388,15 +367,13 @@ void main() {
group('Deleting scores', () { group('Deleting scores', () {
test('deleteScore() ', () async { test('deleteScore() ', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
final deleted = await database.scoreDao.deleteScore( final deleted = await database.scoreEntryDao.deleteScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, roundNumber: 1,
@@ -404,7 +381,7 @@ void main() {
expect(deleted, true); expect(deleted, true);
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, roundNumber: 1,
@@ -414,7 +391,7 @@ void main() {
}); });
test('Deleting a non-existent score returns false', () async { test('Deleting a non-existent score returns false', () async {
final deleted = await database.scoreDao.deleteScore( final deleted = await database.scoreEntryDao.deleteScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, roundNumber: 1,
@@ -424,149 +401,130 @@ void main() {
}); });
test('deleteAllScoresForMatch() works correctly', () async { test('deleteAllScoresForMatch() works correctly', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer2.id, playerId: testPlayer2.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
score: 20,
change: 20,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch2.id, matchId: testMatch2.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 15, change: 15),
score: 15,
change: 15,
); );
final deleted = await database.scoreDao.deleteAllScoresForMatch( final deleted = await database.scoreEntryDao.deleteAllScoresForMatch(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(deleted, true); expect(deleted, true);
final match1Scores = await database.scoreDao.getAllMatchScores( final match1Scores = await database.scoreEntryDao.getAllMatchScores(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(match1Scores.length, 0); expect(match1Scores.length, 0);
final match2Scores = await database.scoreDao.getAllMatchScores( final match2Scores = await database.scoreEntryDao.getAllMatchScores(
matchId: testMatch2.id, matchId: testMatch2.id,
); );
expect(match2Scores.length, 1); expect(match2Scores.length, 1);
}); });
test('deleteAllScoresForPlayerInMatch() works correctly', () async { test('deleteAllScoresForPlayerInMatch() works correctly', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, entry: ScoreEntry(roundNumber: 2, score: 15, change: 5),
score: 15,
change: 5,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer2.id, playerId: testPlayer2.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 6, change: 6),
score: 6,
change: 6,
); );
final deleted = await database.scoreDao.deleteAllScoresForPlayerInMatch( final deleted = await database.scoreEntryDao
playerId: testPlayer1.id, .deleteAllScoresForPlayerInMatch(
matchId: testMatch1.id, playerId: testPlayer1.id,
); matchId: testMatch1.id,
);
expect(deleted, true); expect(deleted, true);
final player1Scores = await database.scoreDao.getAllPlayerScoresInMatch( final player1Scores = await database.scoreEntryDao
playerId: testPlayer1.id, .getAllPlayerScoresInMatch(
matchId: testMatch1.id, playerId: testPlayer1.id,
); matchId: testMatch1.id,
);
expect(player1Scores.length, 0); expect(player1Scores.length, 0);
final player2Scores = await database.scoreDao.getAllPlayerScoresInMatch( final player2Scores = await database.scoreEntryDao
playerId: testPlayer2.id, .getAllPlayerScoresInMatch(
matchId: testMatch1.id, playerId: testPlayer2.id,
); matchId: testMatch1.id,
);
expect(player2Scores.length, 1); expect(player2Scores.length, 1);
}); });
}); });
group('Score Aggregations & Edge Cases', () { group('Score Aggregations & Edge Cases', () {
test('getLatestRoundNumber()', () async { test('getLatestRoundNumber()', () async {
var latestRound = await database.scoreDao.getLatestRoundNumber( var latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(latestRound, isNull); expect(latestRound, isNull);
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
latestRound = await database.scoreDao.getLatestRoundNumber( latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(latestRound, 1); expect(latestRound, 1);
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 5, entry: ScoreEntry(roundNumber: 5, score: 50, change: 40),
score: 50,
change: 40,
); );
latestRound = await database.scoreDao.getLatestRoundNumber( latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(latestRound, 5); expect(latestRound, 5);
}); });
test('getLatestRoundNumber() with non-consecutive rounds', () async { test('getLatestRoundNumber() with non-consecutive rounds', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 5, entry: ScoreEntry(roundNumber: 5, score: 50, change: 40),
score: 50,
change: 40,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 3, entry: ScoreEntry(roundNumber: 3, score: 30, change: 20),
score: 30,
change: 20,
); );
final latestRound = await database.scoreDao.getLatestRoundNumber( final latestRound = await database.scoreEntryDao.getLatestRoundNumber(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -574,35 +532,29 @@ void main() {
}); });
test('getTotalScoreForPlayer()', () async { test('getTotalScoreForPlayer()', () async {
var totalScore = await database.scoreDao.getTotalScoreForPlayer( var totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(totalScore, 0); expect(totalScore, 0);
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, entry: ScoreEntry(roundNumber: 2, score: 25, change: 15),
score: 25,
change: 15,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 3, entry: ScoreEntry(roundNumber: 3, score: 40, change: 15),
score: 40,
change: 15,
); );
totalScore = await database.scoreDao.getTotalScoreForPlayer( totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -610,29 +562,23 @@ void main() {
}); });
test('getTotalScoreForPlayer() ignores round score', () async { test('getTotalScoreForPlayer() ignores round score', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 2, entry: ScoreEntry(roundNumber: 2, score: 25, change: 25),
score: 25,
change: 25,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 25, change: 10),
score: 25,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 3, entry: ScoreEntry(roundNumber: 3, score: 25, change: 25),
score: 25,
change: 25,
); );
final totalScore = await database.scoreDao.getTotalScoreForPlayer( final totalScore = await database.scoreEntryDao.getTotalScoreForPlayer(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
@@ -642,22 +588,18 @@ void main() {
}); });
test('Adding the same score twice replaces the existing one', () async { test('Adding the same score twice replaces the existing one', () async {
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 10, change: 10),
score: 10,
change: 10,
); );
await database.scoreDao.addScore( await database.scoreEntryDao.addScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, entry: ScoreEntry(roundNumber: 1, score: 20, change: 20),
score: 20,
change: 20,
); );
final score = await database.scoreDao.getScore( final score = await database.scoreEntryDao.getScore(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
roundNumber: 1, roundNumber: 1,
@@ -671,100 +613,116 @@ void main() {
group('Handling Winner', () { group('Handling Winner', () {
test('hasWinner() works correctly', () async { test('hasWinner() works correctly', () async {
var hasWinner = await database.scoreDao.hasWinner( var hasWinner = await database.scoreEntryDao.hasWinner(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(hasWinner, false); expect(hasWinner, false);
await database.scoreDao.setWinner( await database.scoreEntryDao.setWinner(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
hasWinner = await database.scoreDao.hasWinner(matchId: testMatch1.id); hasWinner = await database.scoreEntryDao.hasWinner(
matchId: testMatch1.id,
);
expect(hasWinner, true); expect(hasWinner, true);
}); });
test('getWinnersForMatch() returns correct winner', () async { test('getWinnersForMatch() returns correct winner', () async {
var winner = await database.scoreDao.getWinner(matchId: testMatch1.id); var winner = await database.scoreEntryDao.getWinner(
matchId: testMatch1.id,
);
expect(winner, isNull); expect(winner, isNull);
await database.scoreDao.setWinner( await database.scoreEntryDao.setWinner(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
winner = await database.scoreDao.getWinner(matchId: testMatch1.id); winner = await database.scoreEntryDao.getWinner(matchId: testMatch1.id);
expect(winner, isNotNull); expect(winner, isNotNull);
expect(winner!.id, testPlayer1.id); expect(winner!.id, testPlayer1.id);
}); });
test('removeWinner() works correctly', () async { test('removeWinner() works correctly', () async {
var removed = await database.scoreDao.removeWinner( var removed = await database.scoreEntryDao.removeWinner(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(removed, false); expect(removed, false);
await database.scoreDao.setWinner( await database.scoreEntryDao.setWinner(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
removed = await database.scoreDao.removeWinner(matchId: testMatch1.id); removed = await database.scoreEntryDao.removeWinner(
matchId: testMatch1.id,
);
expect(removed, true); expect(removed, true);
var winner = await database.scoreDao.getWinner(matchId: testMatch1.id); var winner = await database.scoreEntryDao.getWinner(
matchId: testMatch1.id,
);
expect(winner, isNull); expect(winner, isNull);
}); });
}); });
group('Handling Looser', () { group('Handling Looser', () {
test('hasLooser() works correctly', () async { test('hasLooser() works correctly', () async {
var hasLooser = await database.scoreDao.hasLooser( var hasLooser = await database.scoreEntryDao.hasLooser(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(hasLooser, false); expect(hasLooser, false);
await database.scoreDao.setLooser( await database.scoreEntryDao.setLooser(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
hasLooser = await database.scoreDao.hasLooser(matchId: testMatch1.id); hasLooser = await database.scoreEntryDao.hasLooser(
matchId: testMatch1.id,
);
expect(hasLooser, true); expect(hasLooser, true);
}); });
test('getLooser() returns correct winner', () async { test('getLooser() returns correct winner', () async {
var looser = await database.scoreDao.getLooser(matchId: testMatch1.id); var looser = await database.scoreEntryDao.getLooser(
matchId: testMatch1.id,
);
expect(looser, isNull); expect(looser, isNull);
await database.scoreDao.setLooser( await database.scoreEntryDao.setLooser(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
looser = await database.scoreDao.getLooser(matchId: testMatch1.id); looser = await database.scoreEntryDao.getLooser(matchId: testMatch1.id);
expect(looser, isNotNull); expect(looser, isNotNull);
expect(looser!.id, testPlayer1.id); expect(looser!.id, testPlayer1.id);
}); });
test('removeLooser() works correctly', () async { test('removeLooser() works correctly', () async {
var removed = await database.scoreDao.removeLooser( var removed = await database.scoreEntryDao.removeLooser(
matchId: testMatch1.id, matchId: testMatch1.id,
); );
expect(removed, false); expect(removed, false);
await database.scoreDao.setLooser( await database.scoreEntryDao.setLooser(
playerId: testPlayer1.id, playerId: testPlayer1.id,
matchId: testMatch1.id, matchId: testMatch1.id,
); );
removed = await database.scoreDao.removeLooser(matchId: testMatch1.id); removed = await database.scoreEntryDao.removeLooser(
matchId: testMatch1.id,
);
expect(removed, true); expect(removed, true);
var looser = await database.scoreDao.getLooser(matchId: testMatch1.id); var looser = await database.scoreEntryDao.getLooser(
matchId: testMatch1.id,
);
expect(looser, isNull); expect(looser, isNull);
}); });
}); });

View File

@@ -12,7 +12,7 @@ import 'package:tallee/data/models/game.dart';
import 'package:tallee/data/models/group.dart'; import 'package:tallee/data/models/group.dart';
import 'package:tallee/data/models/match.dart'; import 'package:tallee/data/models/match.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score.dart'; import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.dart'; import 'package:tallee/data/models/team.dart';
import 'package:tallee/services/data_transfer_service.dart'; import 'package:tallee/services/data_transfer_service.dart';
@@ -65,12 +65,12 @@ void main() {
notes: 'Test notes', notes: 'Test notes',
scores: { scores: {
testPlayer1.id: [ testPlayer1.id: [
Score(roundNumber: 1, score: 10, change: 10), ScoreEntry(roundNumber: 1, score: 10, change: 10),
Score(roundNumber: 2, score: 20, change: 10), ScoreEntry(roundNumber: 2, score: 20, change: 10),
], ],
testPlayer2.id: [ testPlayer2.id: [
Score(roundNumber: 1, score: 15, change: 15), ScoreEntry(roundNumber: 1, score: 15, change: 15),
Score(roundNumber: 2, score: 25, change: 10), ScoreEntry(roundNumber: 2, score: 25, change: 10),
], ],
}, },
); );
@@ -306,12 +306,12 @@ void main() {
(playerId, scoreList) => MapEntry( (playerId, scoreList) => MapEntry(
playerId, playerId,
(scoreList as List) (scoreList as List)
.map((s) => Score.fromJson(s as Map<String, dynamic>)) .map((s) => ScoreEntry.fromJson(s as Map<String, dynamic>))
.toList(), .toList(),
), ),
); );
expect(scores, isA<Map<String, List<Score>>>()); expect(scores, isA<Map<String, List<ScoreEntry>>>());
/* Player 1 scores */ /* Player 1 scores */
// General structure // General structure
@@ -588,6 +588,18 @@ void main() {
expect(games[0].ruleset, testGame.ruleset); expect(games[0].ruleset, testGame.ruleset);
}); });
test('parseGamesFromJson() empty list', () {
final jsonMap = {'games': []};
final games = DataTransferService.parseGamesFromJson(jsonMap);
expect(games, isEmpty);
});
test('parseGamesFromJson() missing key', () {
final jsonMap = <String, dynamic>{};
final games = DataTransferService.parseGamesFromJson(jsonMap);
expect(games, isEmpty);
});
test('parseGroupsFromJson()', () { test('parseGroupsFromJson()', () {
final playerById = { final playerById = {
testPlayer1.id: testPlayer1, testPlayer1.id: testPlayer1,
@@ -619,6 +631,18 @@ void main() {
expect(groups[0].members[1].id, testPlayer2.id); expect(groups[0].members[1].id, testPlayer2.id);
}); });
test('parseGroupsFromJson() empty list', () {
final jsonMap = {'groups': []};
final groups = DataTransferService.parseGroupsFromJson(jsonMap, {});
expect(groups, isEmpty);
});
test('parseGroupsFromJson() missing key', () {
final jsonMap = <String, dynamic>{};
final groups = DataTransferService.parseGroupsFromJson(jsonMap, {});
expect(groups, isEmpty);
});
test('parseGroupsFromJson() ignores invalid player ids', () { test('parseGroupsFromJson() ignores invalid player ids', () {
final playerById = {testPlayer1.id: testPlayer1}; final playerById = {testPlayer1.id: testPlayer1};
@@ -670,6 +694,18 @@ void main() {
expect(teams[0].members[0].id, testPlayer1.id); expect(teams[0].members[0].id, testPlayer1.id);
}); });
test('parseTeamsFromJson() empty list', () {
final jsonMap = {'teams': []};
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
expect(teams, isEmpty);
});
test('parseTeamsFromJson() missing key', () {
final jsonMap = <String, dynamic>{};
final teams = DataTransferService.parseTeamsFromJson(jsonMap, {});
expect(teams, isEmpty);
});
test('parseMatchesFromJson()', () { test('parseMatchesFromJson()', () {
final playerById = { final playerById = {
testPlayer1.id: testPlayer1, testPlayer1.id: testPlayer1,
@@ -707,6 +743,28 @@ void main() {
expect(matches[0].players.length, 2); expect(matches[0].players.length, 2);
}); });
test('parseMatchesFromJson() empty list', () {
final jsonMap = {'teams': []};
final matches = DataTransferService.parseMatchesFromJson(
jsonMap,
{},
{},
{},
);
expect(matches, isEmpty);
});
test('parseMatchesFromJson() missing key', () {
final jsonMap = <String, dynamic>{};
final matches = DataTransferService.parseMatchesFromJson(
jsonMap,
{},
{},
{},
);
expect(matches, isEmpty);
});
test('parseMatchesFromJson() creates unknown game for missing game', () { test('parseMatchesFromJson() creates unknown game for missing game', () {
final playerById = {testPlayer1.id: testPlayer1}; final playerById = {testPlayer1.id: testPlayer1};
final gameById = <String, Game>{}; final gameById = <String, Game>{};
@@ -799,7 +857,7 @@ void main() {
}); });
}); });
testWidgets('validateJsonSchema()', (tester) async { test('validateJsonSchema()', () async {
final validJson = json.encode({ final validJson = json.encode({
'players': [ 'players': [
{ {