feat: implemented team score handling

This commit is contained in:
2026-05-17 23:11:18 +02:00
parent a957408c7e
commit 2f72b71fda
7 changed files with 194 additions and 58 deletions

View File

@@ -4,6 +4,7 @@ import 'package:tallee/data/db/database.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/team_table.dart'; import 'package:tallee/data/db/tables/team_table.dart';
import 'package:tallee/data/models/player.dart'; import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.dart'; import 'package:tallee/data/models/team.dart';
part 'team_dao.g.dart'; part 'team_dao.g.dart';
@@ -186,15 +187,42 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
/// Updates the score of the team with the given [teamId]. /// Updates the score of the team with the given [teamId].
Future<bool> updateTeamScore({ Future<bool> updateTeamScore({
required String teamId, required String teamId,
required String matchId,
required int score, required int score,
}) async { }) async {
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
const TeamTableCompanion(score: Value(null)),
);
await db.scoreEntryDao.deleteAllScoresForMatch(matchId: matchId);
final rowsAffected = final rowsAffected =
await (update(teamTable)..where((t) => t.id.equals(teamId))).write( await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
TeamTableCompanion(score: Value(score)), TeamTableCompanion(score: Value(score)),
); );
final members = await _getTeamMembers(teamId: teamId);
for (final member in members) {
await db.scoreEntryDao.updateScore(
playerId: member.id,
matchId: matchId,
entry: ScoreEntry(score: score),
);
}
return rowsAffected > 0; return rowsAffected > 0;
} }
Future<bool> removeScoreForTeam({
required String teamId,
required String matchId,
}) async {
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
const TeamTableCompanion(score: Value(null)),
);
await db.scoreEntryDao.deleteAllScoresForMatch(matchId: matchId);
return true;
}
/* Delete */ /* Delete */
/// Deletes all teams from the database. /// Deletes all teams from the database.
@@ -212,4 +240,50 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
final rowsAffected = await query.go(); final rowsAffected = await query.go();
return rowsAffected > 0; return rowsAffected > 0;
} }
/* Score handling */
Future<bool> setWinnerTeam({
required String teamId,
required String matchId,
}) async {
return await updateTeamScore(teamId: teamId, matchId: matchId, score: 1);
}
Future<bool> removeWinnerTeam({
required String teamId,
required String matchId,
}) async {
return await removeScoreForTeam(teamId: teamId, matchId: matchId);
}
Future<bool> setLoserTeam({
required String teamId,
required String matchId,
}) async {
return await updateTeamScore(teamId: teamId, matchId: matchId, score: 0);
}
Future<bool> removeLoserTeam({
required String teamId,
required String matchId,
}) async {
return await removeScoreForTeam(teamId: teamId, matchId: matchId);
}
Future<bool> setTeamPlacements({
required String teamId,
required String matchId,
required List<Team> teams,
}) async {
List<bool?> success = List.generate(teams.length, (index) => null);
for (int i = 0; i < teams.length; i++) {
success[i] = await updateTeamScore(
matchId: matchId,
teamId: teams[i].id,
score: teams.length - i,
);
}
return success.every((result) => result == true);
}
} }

View File

@@ -1930,10 +1930,9 @@ class $TeamTableTable extends TeamTable
late final GeneratedColumn<int> score = GeneratedColumn<int>( late final GeneratedColumn<int> score = GeneratedColumn<int>(
'score', 'score',
aliasedName, aliasedName,
false, true,
type: DriftSqlType.int, type: DriftSqlType.int,
requiredDuringInsert: false, requiredDuringInsert: false,
defaultValue: const Constant(0),
); );
@override @override
List<GeneratedColumn> get $columns => [id, name, createdAt, color, score]; List<GeneratedColumn> get $columns => [id, name, createdAt, color, score];
@@ -2010,7 +2009,7 @@ class $TeamTableTable extends TeamTable
score: attachedDatabase.typeMapping.read( score: attachedDatabase.typeMapping.read(
DriftSqlType.int, DriftSqlType.int,
data['${effectivePrefix}score'], data['${effectivePrefix}score'],
)!, ),
); );
} }
@@ -2025,13 +2024,13 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
final String name; final String name;
final DateTime createdAt; final DateTime createdAt;
final String color; final String color;
final int score; final int? score;
const TeamTableData({ const TeamTableData({
required this.id, required this.id,
required this.name, required this.name,
required this.createdAt, required this.createdAt,
required this.color, required this.color,
required this.score, this.score,
}); });
@override @override
Map<String, Expression> toColumns(bool nullToAbsent) { Map<String, Expression> toColumns(bool nullToAbsent) {
@@ -2040,7 +2039,9 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
map['name'] = Variable<String>(name); map['name'] = Variable<String>(name);
map['created_at'] = Variable<DateTime>(createdAt); map['created_at'] = Variable<DateTime>(createdAt);
map['color'] = Variable<String>(color); map['color'] = Variable<String>(color);
map['score'] = Variable<int>(score); if (!nullToAbsent || score != null) {
map['score'] = Variable<int>(score);
}
return map; return map;
} }
@@ -2050,7 +2051,9 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
name: Value(name), name: Value(name),
createdAt: Value(createdAt), createdAt: Value(createdAt),
color: Value(color), color: Value(color),
score: Value(score), score: score == null && nullToAbsent
? const Value.absent()
: Value(score),
); );
} }
@@ -2064,7 +2067,7 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
name: serializer.fromJson<String>(json['name']), name: serializer.fromJson<String>(json['name']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']), createdAt: serializer.fromJson<DateTime>(json['createdAt']),
color: serializer.fromJson<String>(json['color']), color: serializer.fromJson<String>(json['color']),
score: serializer.fromJson<int>(json['score']), score: serializer.fromJson<int?>(json['score']),
); );
} }
@override @override
@@ -2075,7 +2078,7 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
'name': serializer.toJson<String>(name), 'name': serializer.toJson<String>(name),
'createdAt': serializer.toJson<DateTime>(createdAt), 'createdAt': serializer.toJson<DateTime>(createdAt),
'color': serializer.toJson<String>(color), 'color': serializer.toJson<String>(color),
'score': serializer.toJson<int>(score), 'score': serializer.toJson<int?>(score),
}; };
} }
@@ -2084,13 +2087,13 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
String? name, String? name,
DateTime? createdAt, DateTime? createdAt,
String? color, String? color,
int? score, Value<int?> score = const Value.absent(),
}) => TeamTableData( }) => TeamTableData(
id: id ?? this.id, id: id ?? this.id,
name: name ?? this.name, name: name ?? this.name,
createdAt: createdAt ?? this.createdAt, createdAt: createdAt ?? this.createdAt,
color: color ?? this.color, color: color ?? this.color,
score: score ?? this.score, score: score.present ? score.value : this.score,
); );
TeamTableData copyWithCompanion(TeamTableCompanion data) { TeamTableData copyWithCompanion(TeamTableCompanion data) {
return TeamTableData( return TeamTableData(
@@ -2132,7 +2135,7 @@ class TeamTableCompanion extends UpdateCompanion<TeamTableData> {
final Value<String> name; final Value<String> name;
final Value<DateTime> createdAt; final Value<DateTime> createdAt;
final Value<String> color; final Value<String> color;
final Value<int> score; final Value<int?> score;
final Value<int> rowid; final Value<int> rowid;
const TeamTableCompanion({ const TeamTableCompanion({
this.id = const Value.absent(), this.id = const Value.absent(),
@@ -2175,7 +2178,7 @@ class TeamTableCompanion extends UpdateCompanion<TeamTableData> {
Value<String>? name, Value<String>? name,
Value<DateTime>? createdAt, Value<DateTime>? createdAt,
Value<String>? color, Value<String>? color,
Value<int>? score, Value<int?>? score,
Value<int>? rowid, Value<int>? rowid,
}) { }) {
return TeamTableCompanion( return TeamTableCompanion(
@@ -5279,7 +5282,7 @@ typedef $$TeamTableTableCreateCompanionBuilder =
required String name, required String name,
required DateTime createdAt, required DateTime createdAt,
Value<String> color, Value<String> color,
Value<int> score, Value<int?> score,
Value<int> rowid, Value<int> rowid,
}); });
typedef $$TeamTableTableUpdateCompanionBuilder = typedef $$TeamTableTableUpdateCompanionBuilder =
@@ -5288,7 +5291,7 @@ typedef $$TeamTableTableUpdateCompanionBuilder =
Value<String> name, Value<String> name,
Value<DateTime> createdAt, Value<DateTime> createdAt,
Value<String> color, Value<String> color,
Value<int> score, Value<int?> score,
Value<int> rowid, Value<int> rowid,
}); });
@@ -5497,7 +5500,7 @@ class $$TeamTableTableTableManager
Value<String> name = const Value.absent(), Value<String> name = const Value.absent(),
Value<DateTime> createdAt = const Value.absent(), Value<DateTime> createdAt = const Value.absent(),
Value<String> color = const Value.absent(), Value<String> color = const Value.absent(),
Value<int> score = const Value.absent(), Value<int?> score = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => TeamTableCompanion( }) => TeamTableCompanion(
id: id, id: id,
@@ -5513,7 +5516,7 @@ class $$TeamTableTableTableManager
required String name, required String name,
required DateTime createdAt, required DateTime createdAt,
Value<String> color = const Value.absent(), Value<String> color = const Value.absent(),
Value<int> score = const Value.absent(), Value<int?> score = const Value.absent(),
Value<int> rowid = const Value.absent(), Value<int> rowid = const Value.absent(),
}) => TeamTableCompanion.insert( }) => TeamTableCompanion.insert(
id: id, id: id,

View File

@@ -5,7 +5,7 @@ class TeamTable extends Table {
TextColumn get name => text()(); TextColumn get name => text()();
DateTimeColumn get createdAt => dateTime()(); DateTimeColumn get createdAt => dateTime()();
TextColumn get color => text().withDefault(const Constant('blue'))(); TextColumn get color => text().withDefault(const Constant('blue'))();
IntColumn get score => integer().withDefault(const Constant(0))(); IntColumn get score => integer().nullable()();
@override @override
Set<Column<Object>> get primaryKey => {id}; Set<Column<Object>> get primaryKey => {id};

View File

@@ -231,8 +231,13 @@ class Match {
} }
List<Team> _getHighestScoreTeam() { List<Team> _getHighestScoreTeam() {
if (teams!.every((team) => team.score == null)) {
return [];
}
final int highestScore = teams! final int highestScore = teams!
.map((team) => team.score) .map((team) => team.score)
.whereType<int>()
.reduce((max, score) => score > max ? score : max); .reduce((max, score) => score > max ? score : max);
return teams!.where((team) { return teams!.where((team) {
@@ -241,8 +246,13 @@ class Match {
} }
List<Team> _getLowestScoreTeam() { List<Team> _getLowestScoreTeam() {
if (teams!.every((team) => team.score == null)) {
return [];
}
final int lowestScore = teams! final int lowestScore = teams!
.map((team) => team.score) .map((team) => team.score)
.whereType<int>()
.reduce((min, score) => score < min ? score : min); .reduce((min, score) => score < min ? score : min);
return teams!.where((team) { return teams!.where((team) {

View File

@@ -9,7 +9,7 @@ class Team {
final String name; final String name;
final DateTime createdAt; final DateTime createdAt;
final GameColor color; final GameColor color;
final int score; final int? score;
final List<Player> members; final List<Player> members;
Team({ Team({
@@ -17,7 +17,7 @@ class Team {
required this.name, required this.name,
DateTime? createdAt, DateTime? createdAt,
this.color = GameColor.blue, this.color = GameColor.blue,
this.score = 0, this.score,
required this.members, required this.members,
}) : id = id ?? const Uuid().v4(), }) : id = id ?? const Uuid().v4(),
createdAt = createdAt ?? clock.now(); createdAt = createdAt ?? clock.now();

View File

@@ -198,20 +198,7 @@ class _MatchResultViewState extends State<MatchResultView> {
} }
void initializeAsTeamMatch() { void initializeAsTeamMatch() {
allTeams = allTeams = widget.match.teams ?? [];
widget.match.teams ??
List.generate(
4,
(index) => Team(
name: 'Team ${index + 1}',
members: [
Player(name: 'Player ${index + 1}'),
Player(name: 'Player ${index + 2}'),
Player(name: 'Player ${index + 3}'),
Player(name: 'Player ${index + 4}'),
],
),
);
allTeams.sort((a, b) => a.name.compareTo(b.name)); allTeams.sort((a, b) => a.name.compareTo(b.name));
controller = List.generate( controller = List.generate(
@@ -220,7 +207,26 @@ class _MatchResultViewState extends State<MatchResultView> {
); );
// Prefill fields // Prefill fields
//TODO if (widget.match.mvt.isNotEmpty) {
if (rulesetSupportsWinnerSelection()) {
_selectedTeam = allTeams.firstWhere(
(p) => p.id == widget.match.mvt.first.id,
);
} else if (rulesetSupportsScoreEntry()) {
for (int i = 0; i < allTeams.length; i++) {
final score = allTeams[i].score;
controller[i].text = score.toString();
}
} else if (rulesetSupportsPlacement()) {
allTeams.sort((a, b) {
final scoreA =
allTeams.where((team) => a.id == team.id).first.score ?? 0;
final scoreB =
allTeams.where((team) => b.id == team.id).first.score ?? 0;
return scoreB.compareTo(scoreA);
});
}
}
} }
void inizializeAsNormalMatch() { void inizializeAsNormalMatch() {
@@ -282,41 +288,84 @@ class _MatchResultViewState extends State<MatchResultView> {
/// Handles saving or removing the winner in the database. /// Handles saving or removing the winner in the database.
Future<bool> _handleWinner() async { Future<bool> _handleWinner() async {
if (_selectedPlayer == null) { if (isTeamMatch) {
return await db.scoreEntryDao.removeWinner(matchId: widget.match.id); if (_selectedTeam == null) {
return await db.teamDao.setWinnerTeam(
matchId: widget.match.id,
teamId: _selectedTeam!.id,
);
} else {
return await db.teamDao.setLoserTeam(
matchId: widget.match.id,
teamId: _selectedTeam!.id,
);
}
} else { } else {
return await db.scoreEntryDao.setWinner( if (_selectedPlayer == null) {
matchId: widget.match.id, return await db.scoreEntryDao.removeWinner(matchId: widget.match.id);
playerId: _selectedPlayer!.id, } else {
); return await db.scoreEntryDao.setWinner(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
}
} }
} }
/// Handles saving or removing the loser in the database. /// Handles saving or removing the loser in the database.
Future<bool> _handleLoser() async { Future<bool> _handleLoser() async {
if (_selectedPlayer == null) { if (isTeamMatch) {
return await db.scoreEntryDao.removeLoser(matchId: widget.match.id); if (_selectedTeam == null) {
return await db.teamDao.removeLoserTeam(
matchId: widget.match.id,
teamId: _selectedTeam!.id,
);
} else {
return await db.teamDao.setLoserTeam(
matchId: widget.match.id,
teamId: _selectedTeam!.id,
);
}
} else { } else {
return await db.scoreEntryDao.setLoser( if (_selectedPlayer == null) {
matchId: widget.match.id, return await db.scoreEntryDao.removeLoser(matchId: widget.match.id);
playerId: _selectedPlayer!.id, } else {
); return await db.scoreEntryDao.setLoser(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
}
} }
} }
/// Handles saving the scores for each player in the database. /// Handles saving the scores for each player in the database.
Future<void> _handleScores() async { Future<void> _handleScores() async {
for (int i = 0; i < allPlayers.length; i++) { if (isTeamMatch) {
var text = controller[i].text; for (int i = 0; i < allTeams.length; i++) {
if (text.isEmpty) { var text = controller[i].text;
text = '0'; if (text.isEmpty) {
text = '0';
}
final score = int.parse(text);
await db.teamDao.updateTeamScore(
matchId: widget.match.id,
teamId: allTeams[i].id,
score: score,
);
}
} else {
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.scoreEntryDao.addScore(
matchId: widget.match.id,
playerId: allPlayers[i].id,
entry: ScoreEntry(roundNumber: 0, score: score, change: 0),
);
} }
final score = int.parse(text);
await db.scoreEntryDao.addScore(
matchId: widget.match.id,
playerId: allPlayers[i].id,
entry: ScoreEntry(roundNumber: 0, score: score, change: 0),
);
} }
} }

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.30+281 version: 0.0.30+285
environment: environment:
sdk: ^3.8.1 sdk: ^3.8.1