diff --git a/assets/schema.json b/assets/schema.json index 252fb24..8021012 100644 --- a/assets/schema.json +++ b/assets/schema.json @@ -142,6 +142,9 @@ "createdAt": { "type": "string" }, + "endedAt": { + "type": ["string", "null"] + }, "gameId": { "type": "string" }, diff --git a/lib/data/dao/match_dao.dart b/lib/data/dao/match_dao.dart index 201fc52..3f5a7a1 100644 --- a/lib/data/dao/match_dao.dart +++ b/lib/data/dao/match_dao.dart @@ -38,6 +38,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { players: players, notes: row.notes ?? '', createdAt: row.createdAt, + endedAt: row.endedAt, ); }), ); @@ -68,6 +69,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { players: players, notes: result.notes ?? '', createdAt: result.createdAt, + endedAt: result.endedAt, ); } @@ -84,6 +86,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { name: Value(match.name), notes: Value(match.notes), createdAt: match.createdAt, + endedAt: Value(match.endedAt), ), mode: InsertMode.insertOrReplace, ); @@ -166,6 +169,7 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { name: Value(match.name), notes: Value(match.notes), createdAt: match.createdAt, + endedAt: Value(match.endedAt), ), ) .toList(), @@ -346,6 +350,20 @@ class MatchDao extends DatabaseAccessor with _$MatchDaoMixin { return rowsAffected > 0; } + /// Updates the endedAt timestamp of the match with the given [matchId]. + /// Pass null to remove the ended time (mark match as ongoing). + /// Returns `true` if more than 0 rows were affected, otherwise `false`. + Future updateMatchEndedAt({ + required String matchId, + required DateTime? endedAt, + }) async { + final query = update(matchTable)..where((g) => g.id.equals(matchId)); + final rowsAffected = await query.write( + MatchTableCompanion(endedAt: Value(endedAt)), + ); + return rowsAffected > 0; + } + // ============================================================ // TEMPORARY: Winner methods - these are stubs and do not persist data // TODO: Implement proper winner handling diff --git a/lib/data/db/database.g.dart b/lib/data/db/database.g.dart index a11c224..fe14e93 100644 --- a/lib/data/db/database.g.dart +++ b/lib/data/db/database.g.dart @@ -1155,6 +1155,17 @@ class $MatchTableTable extends MatchTable type: DriftSqlType.dateTime, requiredDuringInsert: true, ); + static const VerificationMeta _endedAtMeta = const VerificationMeta( + 'endedAt', + ); + @override + late final GeneratedColumn endedAt = GeneratedColumn( + 'ended_at', + aliasedName, + true, + type: DriftSqlType.dateTime, + requiredDuringInsert: false, + ); @override List get $columns => [ id, @@ -1163,6 +1174,7 @@ class $MatchTableTable extends MatchTable name, notes, createdAt, + endedAt, ]; @override String get aliasedName => _alias ?? actualTableName; @@ -1215,6 +1227,12 @@ class $MatchTableTable extends MatchTable } else if (isInserting) { context.missing(_createdAtMeta); } + if (data.containsKey('ended_at')) { + context.handle( + _endedAtMeta, + endedAt.isAcceptableOrUnknown(data['ended_at']!, _endedAtMeta), + ); + } return context; } @@ -1248,6 +1266,10 @@ class $MatchTableTable extends MatchTable DriftSqlType.dateTime, data['${effectivePrefix}created_at'], )!, + endedAt: attachedDatabase.typeMapping.read( + DriftSqlType.dateTime, + data['${effectivePrefix}ended_at'], + ), ); } @@ -1264,6 +1286,7 @@ class MatchTableData extends DataClass implements Insertable { final String? name; final String? notes; final DateTime createdAt; + final DateTime? endedAt; const MatchTableData({ required this.id, required this.gameId, @@ -1271,6 +1294,7 @@ class MatchTableData extends DataClass implements Insertable { this.name, this.notes, required this.createdAt, + this.endedAt, }); @override Map toColumns(bool nullToAbsent) { @@ -1287,6 +1311,9 @@ class MatchTableData extends DataClass implements Insertable { map['notes'] = Variable(notes); } map['created_at'] = Variable(createdAt); + if (!nullToAbsent || endedAt != null) { + map['ended_at'] = Variable(endedAt); + } return map; } @@ -1302,6 +1329,9 @@ class MatchTableData extends DataClass implements Insertable { ? const Value.absent() : Value(notes), createdAt: Value(createdAt), + endedAt: endedAt == null && nullToAbsent + ? const Value.absent() + : Value(endedAt), ); } @@ -1317,6 +1347,7 @@ class MatchTableData extends DataClass implements Insertable { name: serializer.fromJson(json['name']), notes: serializer.fromJson(json['notes']), createdAt: serializer.fromJson(json['createdAt']), + endedAt: serializer.fromJson(json['endedAt']), ); } @override @@ -1329,6 +1360,7 @@ class MatchTableData extends DataClass implements Insertable { 'name': serializer.toJson(name), 'notes': serializer.toJson(notes), 'createdAt': serializer.toJson(createdAt), + 'endedAt': serializer.toJson(endedAt), }; } @@ -1339,6 +1371,7 @@ class MatchTableData extends DataClass implements Insertable { Value name = const Value.absent(), Value notes = const Value.absent(), DateTime? createdAt, + Value endedAt = const Value.absent(), }) => MatchTableData( id: id ?? this.id, gameId: gameId ?? this.gameId, @@ -1346,6 +1379,7 @@ class MatchTableData extends DataClass implements Insertable { name: name.present ? name.value : this.name, notes: notes.present ? notes.value : this.notes, createdAt: createdAt ?? this.createdAt, + endedAt: endedAt.present ? endedAt.value : this.endedAt, ); MatchTableData copyWithCompanion(MatchTableCompanion data) { return MatchTableData( @@ -1355,6 +1389,7 @@ class MatchTableData extends DataClass implements Insertable { name: data.name.present ? data.name.value : this.name, notes: data.notes.present ? data.notes.value : this.notes, createdAt: data.createdAt.present ? data.createdAt.value : this.createdAt, + endedAt: data.endedAt.present ? data.endedAt.value : this.endedAt, ); } @@ -1366,13 +1401,15 @@ class MatchTableData extends DataClass implements Insertable { ..write('groupId: $groupId, ') ..write('name: $name, ') ..write('notes: $notes, ') - ..write('createdAt: $createdAt') + ..write('createdAt: $createdAt, ') + ..write('endedAt: $endedAt') ..write(')')) .toString(); } @override - int get hashCode => Object.hash(id, gameId, groupId, name, notes, createdAt); + int get hashCode => + Object.hash(id, gameId, groupId, name, notes, createdAt, endedAt); @override bool operator ==(Object other) => identical(this, other) || @@ -1382,7 +1419,8 @@ class MatchTableData extends DataClass implements Insertable { other.groupId == this.groupId && other.name == this.name && other.notes == this.notes && - other.createdAt == this.createdAt); + other.createdAt == this.createdAt && + other.endedAt == this.endedAt); } class MatchTableCompanion extends UpdateCompanion { @@ -1392,6 +1430,7 @@ class MatchTableCompanion extends UpdateCompanion { final Value name; final Value notes; final Value createdAt; + final Value endedAt; final Value rowid; const MatchTableCompanion({ this.id = const Value.absent(), @@ -1400,6 +1439,7 @@ class MatchTableCompanion extends UpdateCompanion { this.name = const Value.absent(), this.notes = const Value.absent(), this.createdAt = const Value.absent(), + this.endedAt = const Value.absent(), this.rowid = const Value.absent(), }); MatchTableCompanion.insert({ @@ -1409,6 +1449,7 @@ class MatchTableCompanion extends UpdateCompanion { this.name = const Value.absent(), this.notes = const Value.absent(), required DateTime createdAt, + this.endedAt = const Value.absent(), this.rowid = const Value.absent(), }) : id = Value(id), gameId = Value(gameId), @@ -1420,6 +1461,7 @@ class MatchTableCompanion extends UpdateCompanion { Expression? name, Expression? notes, Expression? createdAt, + Expression? endedAt, Expression? rowid, }) { return RawValuesInsertable({ @@ -1429,6 +1471,7 @@ class MatchTableCompanion extends UpdateCompanion { if (name != null) 'name': name, if (notes != null) 'notes': notes, if (createdAt != null) 'created_at': createdAt, + if (endedAt != null) 'ended_at': endedAt, if (rowid != null) 'rowid': rowid, }); } @@ -1440,6 +1483,7 @@ class MatchTableCompanion extends UpdateCompanion { Value? name, Value? notes, Value? createdAt, + Value? endedAt, Value? rowid, }) { return MatchTableCompanion( @@ -1449,6 +1493,7 @@ class MatchTableCompanion extends UpdateCompanion { name: name ?? this.name, notes: notes ?? this.notes, createdAt: createdAt ?? this.createdAt, + endedAt: endedAt ?? this.endedAt, rowid: rowid ?? this.rowid, ); } @@ -1474,6 +1519,9 @@ class MatchTableCompanion extends UpdateCompanion { if (createdAt.present) { map['created_at'] = Variable(createdAt.value); } + if (endedAt.present) { + map['ended_at'] = Variable(endedAt.value); + } if (rowid.present) { map['rowid'] = Variable(rowid.value); } @@ -1489,6 +1537,7 @@ class MatchTableCompanion extends UpdateCompanion { ..write('name: $name, ') ..write('notes: $notes, ') ..write('createdAt: $createdAt, ') + ..write('endedAt: $endedAt, ') ..write('rowid: $rowid') ..write(')')) .toString(); @@ -4005,6 +4054,7 @@ typedef $$MatchTableTableCreateCompanionBuilder = Value name, Value notes, required DateTime createdAt, + Value endedAt, Value rowid, }); typedef $$MatchTableTableUpdateCompanionBuilder = @@ -4015,6 +4065,7 @@ typedef $$MatchTableTableUpdateCompanionBuilder = Value name, Value notes, Value createdAt, + Value endedAt, Value rowid, }); @@ -4129,6 +4180,11 @@ class $$MatchTableTableFilterComposer builder: (column) => ColumnFilters(column), ); + ColumnFilters get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnFilters(column), + ); + $$GameTableTableFilterComposer get gameId { final $$GameTableTableFilterComposer composer = $composerBuilder( composer: this, @@ -4255,6 +4311,11 @@ class $$MatchTableTableOrderingComposer builder: (column) => ColumnOrderings(column), ); + ColumnOrderings get endedAt => $composableBuilder( + column: $table.endedAt, + builder: (column) => ColumnOrderings(column), + ); + $$GameTableTableOrderingComposer get gameId { final $$GameTableTableOrderingComposer composer = $composerBuilder( composer: this, @@ -4323,6 +4384,9 @@ class $$MatchTableTableAnnotationComposer GeneratedColumn get createdAt => $composableBuilder(column: $table.createdAt, builder: (column) => column); + GeneratedColumn get endedAt => + $composableBuilder(column: $table.endedAt, builder: (column) => column); + $$GameTableTableAnnotationComposer get gameId { final $$GameTableTableAnnotationComposer composer = $composerBuilder( composer: this, @@ -4459,6 +4523,7 @@ class $$MatchTableTableTableManager Value name = const Value.absent(), Value notes = const Value.absent(), Value createdAt = const Value.absent(), + Value endedAt = const Value.absent(), Value rowid = const Value.absent(), }) => MatchTableCompanion( id: id, @@ -4467,6 +4532,7 @@ class $$MatchTableTableTableManager name: name, notes: notes, createdAt: createdAt, + endedAt: endedAt, rowid: rowid, ), createCompanionCallback: @@ -4477,6 +4543,7 @@ class $$MatchTableTableTableManager Value name = const Value.absent(), Value notes = const Value.absent(), required DateTime createdAt, + Value endedAt = const Value.absent(), Value rowid = const Value.absent(), }) => MatchTableCompanion.insert( id: id, @@ -4485,6 +4552,7 @@ class $$MatchTableTableTableManager name: name, notes: notes, createdAt: createdAt, + endedAt: endedAt, rowid: rowid, ), withReferenceMapper: (p0) => p0 diff --git a/lib/data/db/tables/match_table.dart b/lib/data/db/tables/match_table.dart index 0dd0eed..83b6897 100644 --- a/lib/data/db/tables/match_table.dart +++ b/lib/data/db/tables/match_table.dart @@ -11,6 +11,7 @@ class MatchTable extends Table { TextColumn get name => text().nullable()(); TextColumn get notes => text().nullable()(); DateTimeColumn get createdAt => dateTime()(); + DateTimeColumn get endedAt => dateTime().nullable()(); @override Set> get primaryKey => {id}; diff --git a/lib/data/dto/match.dart b/lib/data/dto/match.dart index 20852a3..4f919b4 100644 --- a/lib/data/dto/match.dart +++ b/lib/data/dto/match.dart @@ -8,6 +8,7 @@ import 'package:uuid/uuid.dart'; class Match { final String id; final DateTime createdAt; + final DateTime? endedAt; final String name; final Game game; final Group? group; @@ -18,6 +19,7 @@ class Match { Match({ String? id, DateTime? createdAt, + this.endedAt, required this.name, required this.game, this.group, @@ -29,7 +31,7 @@ class Match { @override String toString() { - return 'Match{id: $id, name: $name, game: $game, group: $group, players: $players, notes: $notes}'; + return 'Match{id: $id, name: $name, game: $game, group: $group, players: $players, notes: $notes, endedAt: $endedAt}'; } /// Creates a Match instance from a JSON object (ID references format). @@ -37,6 +39,7 @@ class Match { Match.fromJson(Map json) : id = json['id'], createdAt = DateTime.parse(json['createdAt']), + endedAt = json['endedAt'] != null ? DateTime.parse(json['endedAt']) : null, name = json['name'], game = Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: '', icon: ''), // Populated during import via DataTransferService group = null, // Populated during import via DataTransferService @@ -47,6 +50,7 @@ class Match { Map toJson() => { 'id': id, 'createdAt': createdAt.toIso8601String(), + 'endedAt': endedAt?.toIso8601String(), 'name': name, 'gameId': game.id, 'groupId': group?.id, diff --git a/lib/services/data_transfer_service.dart b/lib/services/data_transfer_service.dart index 9cb38ce..fb69e12 100644 --- a/lib/services/data_transfer_service.dart +++ b/lib/services/data_transfer_service.dart @@ -61,6 +61,7 @@ class DataTransferService { 'id': m.id, 'name': m.name, 'createdAt': m.createdAt.toIso8601String(), + 'endedAt': m.endedAt?.toIso8601String(), 'gameId': m.game.id, 'groupId': m.group?.id, 'playerIds': (m.players ?? []).map((p) => p.id).toList(), @@ -195,6 +196,7 @@ class DataTransferService { final String gameId = map['gameId'] as String; final String? groupId = map['groupId'] as String?; final List playerIds = (map['playerIds'] as List? ?? []).cast(); + final DateTime? endedAt = map['endedAt'] != null ? DateTime.parse(map['endedAt'] as String) : null; final game = gameById[gameId]; final group = (groupId == null) ? null : groupById[groupId]; @@ -210,6 +212,7 @@ class DataTransferService { group: group, players: players.isNotEmpty ? players : null, createdAt: DateTime.parse(map['createdAt'] as String), + endedAt: endedAt, notes: map['notes'] as String? ?? '', ); }).toList();