feat: games with match associations cant be deleted
This commit is contained in:
@@ -176,4 +176,25 @@ class GameDao extends DatabaseAccessor<AppDatabase> with _$GameDaoMixin {
|
|||||||
final rowsAffected = await query.go();
|
final rowsAffected = await query.go();
|
||||||
return rowsAffected > 0;
|
return rowsAffected > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves all games with their respective match counts.
|
||||||
|
/// Returns a list of tuples (Game, matchCount).
|
||||||
|
Future<List<(Game, int)>> getGameUsage() async {
|
||||||
|
final games = await getAllGames();
|
||||||
|
|
||||||
|
final results = <(Game, int)>[];
|
||||||
|
|
||||||
|
for (final game in games) {
|
||||||
|
final matchCount =
|
||||||
|
await (selectOnly(db.matchTable)
|
||||||
|
..where(db.matchTable.gameId.equals(game.id))
|
||||||
|
..addColumns([db.matchTable.id.count()]))
|
||||||
|
.map((row) => row.read(db.matchTable.id.count()))
|
||||||
|
.getSingle();
|
||||||
|
|
||||||
|
results.add((game, matchCount ?? 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
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/data/db/database.dart';
|
||||||
import 'package:tallee/data/models/game.dart';
|
import 'package:tallee/data/models/game.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game/create_game_view.dart';
|
import 'package:tallee/presentation/views/main_menu/match_view/create_match/create_game/create_game_view.dart';
|
||||||
@@ -34,6 +36,10 @@ class ChooseGameView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _ChooseGameViewState extends State<ChooseGameView> {
|
class _ChooseGameViewState extends State<ChooseGameView> {
|
||||||
|
late final AppDatabase db;
|
||||||
|
|
||||||
|
late List<(Game, int)> gameCounts = [];
|
||||||
|
|
||||||
/// Controller for the search bar
|
/// Controller for the search bar
|
||||||
final TextEditingController searchBarController = TextEditingController();
|
final TextEditingController searchBarController = TextEditingController();
|
||||||
|
|
||||||
@@ -45,6 +51,9 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
fetchGameCounts();
|
||||||
|
|
||||||
selectedGameId = widget.initialGameId;
|
selectedGameId = widget.initialGameId;
|
||||||
|
|
||||||
// Start with all games visible
|
// Start with all games visible
|
||||||
@@ -150,6 +159,7 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
|||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
builder: (context) => CreateGameView(
|
builder: (context) => CreateGameView(
|
||||||
gameToEdit: game,
|
gameToEdit: game,
|
||||||
|
canDelete: canDeleteGame(game),
|
||||||
onGameChanged: () {
|
onGameChanged: () {
|
||||||
widget.onGamesUpdated?.call();
|
widget.onGamesUpdated?.call();
|
||||||
},
|
},
|
||||||
@@ -209,4 +219,16 @@ class _ChooseGameViewState extends State<ChooseGameView> {
|
|||||||
void _refreshFromSource() {
|
void _refreshFromSource() {
|
||||||
_applySearchFilter(searchBarController.text);
|
_applySearchFilter(searchBarController.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> fetchGameCounts() async {
|
||||||
|
gameCounts = await db.gameDao.getGameUsage();
|
||||||
|
}
|
||||||
|
|
||||||
|
// A game can only be deleted if there are no matches using it
|
||||||
|
bool canDeleteGame(Game game) {
|
||||||
|
final count = gameCounts
|
||||||
|
.firstWhere((gc) => gc.$1.id == game.id, orElse: () => (game, 0))
|
||||||
|
.$2;
|
||||||
|
return count == 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,18 @@ import 'package:tallee/presentation/widgets/tiles/choose_tile.dart';
|
|||||||
class CreateGameView extends StatefulWidget {
|
class CreateGameView extends StatefulWidget {
|
||||||
const CreateGameView({
|
const CreateGameView({
|
||||||
super.key,
|
super.key,
|
||||||
this.gameToEdit,
|
|
||||||
required this.onGameChanged,
|
required this.onGameChanged,
|
||||||
|
this.gameToEdit,
|
||||||
|
this.canDelete = false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Callback to invoke when the game is created or edited
|
||||||
|
final VoidCallback onGameChanged;
|
||||||
|
|
||||||
/// An optional game to prefill the fields
|
/// An optional game to prefill the fields
|
||||||
final Game? gameToEdit;
|
final Game? gameToEdit;
|
||||||
|
|
||||||
/// Callback to invoke when the game is created or edited
|
final bool canDelete;
|
||||||
final VoidCallback onGameChanged;
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CreateGameView> createState() => _CreateGameViewState();
|
State<CreateGameView> createState() => _CreateGameViewState();
|
||||||
@@ -41,7 +44,6 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
/// GlobalKey for ScaffoldMessenger to show snackbars
|
/// GlobalKey for ScaffoldMessenger to show snackbars
|
||||||
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
|
||||||
|
|
||||||
/// The database instance for accessing game data.
|
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
|
||||||
/// The currently selected ruleset for the game.
|
/// The currently selected ruleset for the game.
|
||||||
@@ -133,13 +135,12 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(isEditing ? loc.edit_game : loc.create_game),
|
title: Text(isEditing ? loc.edit_game : loc.create_game),
|
||||||
actions: widget.gameToEdit == null
|
actions: [
|
||||||
? []
|
if (isEditMode())
|
||||||
: [
|
IconButton(
|
||||||
IconButton(
|
icon: const Icon(Icons.delete),
|
||||||
icon: const Icon(Icons.delete),
|
onPressed: widget.canDelete
|
||||||
onPressed: () async {
|
? () async {
|
||||||
if (widget.gameToEdit != null) {
|
|
||||||
showDialog<bool>(
|
showDialog<bool>(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => CustomAlertDialog(
|
builder: (context) => CustomAlertDialog(
|
||||||
@@ -176,9 +177,9 @@ class _CreateGameViewState extends State<CreateGameView> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
: null,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
|
|||||||
Reference in New Issue
Block a user