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/team_table.dart';
import 'package:tallee/data/models/player.dart';
import 'package:tallee/data/models/score_entry.dart';
import 'package:tallee/data/models/team.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].
Future<bool> updateTeamScore({
required String teamId,
required String matchId,
required int score,
}) async {
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
const TeamTableCompanion(score: Value(null)),
);
await db.scoreEntryDao.deleteAllScoresForMatch(matchId: matchId);
final rowsAffected =
await (update(teamTable)..where((t) => t.id.equals(teamId))).write(
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;
}
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 */
/// Deletes all teams from the database.
@@ -212,4 +240,50 @@ class TeamDao extends DatabaseAccessor<AppDatabase> with _$TeamDaoMixin {
final rowsAffected = await query.go();
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>(
'score',
aliasedName,
false,
true,
type: DriftSqlType.int,
requiredDuringInsert: false,
defaultValue: const Constant(0),
);
@override
List<GeneratedColumn> get $columns => [id, name, createdAt, color, score];
@@ -2010,7 +2009,7 @@ class $TeamTableTable extends TeamTable
score: attachedDatabase.typeMapping.read(
DriftSqlType.int,
data['${effectivePrefix}score'],
)!,
),
);
}
@@ -2025,13 +2024,13 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
final String name;
final DateTime createdAt;
final String color;
final int score;
final int? score;
const TeamTableData({
required this.id,
required this.name,
required this.createdAt,
required this.color,
required this.score,
this.score,
});
@override
Map<String, Expression> toColumns(bool nullToAbsent) {
@@ -2040,7 +2039,9 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
map['name'] = Variable<String>(name);
map['created_at'] = Variable<DateTime>(createdAt);
map['color'] = Variable<String>(color);
map['score'] = Variable<int>(score);
if (!nullToAbsent || score != null) {
map['score'] = Variable<int>(score);
}
return map;
}
@@ -2050,7 +2051,9 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
name: Value(name),
createdAt: Value(createdAt),
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']),
createdAt: serializer.fromJson<DateTime>(json['createdAt']),
color: serializer.fromJson<String>(json['color']),
score: serializer.fromJson<int>(json['score']),
score: serializer.fromJson<int?>(json['score']),
);
}
@override
@@ -2075,7 +2078,7 @@ class TeamTableData extends DataClass implements Insertable<TeamTableData> {
'name': serializer.toJson<String>(name),
'createdAt': serializer.toJson<DateTime>(createdAt),
'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,
DateTime? createdAt,
String? color,
int? score,
Value<int?> score = const Value.absent(),
}) => TeamTableData(
id: id ?? this.id,
name: name ?? this.name,
createdAt: createdAt ?? this.createdAt,
color: color ?? this.color,
score: score ?? this.score,
score: score.present ? score.value : this.score,
);
TeamTableData copyWithCompanion(TeamTableCompanion data) {
return TeamTableData(
@@ -2132,7 +2135,7 @@ class TeamTableCompanion extends UpdateCompanion<TeamTableData> {
final Value<String> name;
final Value<DateTime> createdAt;
final Value<String> color;
final Value<int> score;
final Value<int?> score;
final Value<int> rowid;
const TeamTableCompanion({
this.id = const Value.absent(),
@@ -2175,7 +2178,7 @@ class TeamTableCompanion extends UpdateCompanion<TeamTableData> {
Value<String>? name,
Value<DateTime>? createdAt,
Value<String>? color,
Value<int>? score,
Value<int?>? score,
Value<int>? rowid,
}) {
return TeamTableCompanion(
@@ -5279,7 +5282,7 @@ typedef $$TeamTableTableCreateCompanionBuilder =
required String name,
required DateTime createdAt,
Value<String> color,
Value<int> score,
Value<int?> score,
Value<int> rowid,
});
typedef $$TeamTableTableUpdateCompanionBuilder =
@@ -5288,7 +5291,7 @@ typedef $$TeamTableTableUpdateCompanionBuilder =
Value<String> name,
Value<DateTime> createdAt,
Value<String> color,
Value<int> score,
Value<int?> score,
Value<int> rowid,
});
@@ -5497,7 +5500,7 @@ class $$TeamTableTableTableManager
Value<String> name = const Value.absent(),
Value<DateTime> createdAt = 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(),
}) => TeamTableCompanion(
id: id,
@@ -5513,7 +5516,7 @@ class $$TeamTableTableTableManager
required String name,
required DateTime createdAt,
Value<String> color = const Value.absent(),
Value<int> score = const Value.absent(),
Value<int?> score = const Value.absent(),
Value<int> rowid = const Value.absent(),
}) => TeamTableCompanion.insert(
id: id,

View File

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

View File

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

View File

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

View File

@@ -198,20 +198,7 @@ class _MatchResultViewState extends State<MatchResultView> {
}
void initializeAsTeamMatch() {
allTeams =
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 = widget.match.teams ?? [];
allTeams.sort((a, b) => a.name.compareTo(b.name));
controller = List.generate(
@@ -220,7 +207,26 @@ class _MatchResultViewState extends State<MatchResultView> {
);
// 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() {
@@ -282,41 +288,84 @@ class _MatchResultViewState extends State<MatchResultView> {
/// Handles saving or removing the winner in the database.
Future<bool> _handleWinner() async {
if (_selectedPlayer == null) {
return await db.scoreEntryDao.removeWinner(matchId: widget.match.id);
if (isTeamMatch) {
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 {
return await db.scoreEntryDao.setWinner(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
if (_selectedPlayer == null) {
return await db.scoreEntryDao.removeWinner(matchId: widget.match.id);
} else {
return await db.scoreEntryDao.setWinner(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
}
}
}
/// Handles saving or removing the loser in the database.
Future<bool> _handleLoser() async {
if (_selectedPlayer == null) {
return await db.scoreEntryDao.removeLoser(matchId: widget.match.id);
if (isTeamMatch) {
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 {
return await db.scoreEntryDao.setLoser(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
if (_selectedPlayer == null) {
return await db.scoreEntryDao.removeLoser(matchId: widget.match.id);
} else {
return await db.scoreEntryDao.setLoser(
matchId: widget.match.id,
playerId: _selectedPlayer!.id,
);
}
}
}
/// Handles saving the scores for each player in the database.
Future<void> _handleScores() async {
for (int i = 0; i < allPlayers.length; i++) {
var text = controller[i].text;
if (text.isEmpty) {
text = '0';
if (isTeamMatch) {
for (int i = 0; i < allTeams.length; i++) {
var text = controller[i].text;
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
description: "Tracking App for Card Games"
publish_to: 'none'
version: 0.0.30+281
version: 0.0.30+285
environment:
sdk: ^3.8.1