add player change callbacks and improve player detail view
Some checks failed
Pull Request Pipeline / test (pull_request) Failing after 47s
Pull Request Pipeline / lint (pull_request) Failing after 53s

This commit is contained in:
2026-05-20 19:50:34 +02:00
parent b305145d34
commit 869c70ff63
6 changed files with 102 additions and 30 deletions

View File

@@ -169,15 +169,18 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
.map((row) => row.name) .map((row) => row.name)
.getSingleOrNull() ?? .getSingleOrNull() ??
''; '';
final previousNameCount = await getNameCount(name: previousPlayerName); final previousNameCount = (await getNameCount(name: previousPlayerName))!;
print('previousNameCount: $previousNameCount');
// Update name count for the new name
final count = await calculateNameCount(name: name);
print('count: $count');
final rowsAffected = final rowsAffected =
await (update(playerTable)..where((p) => p.id.equals(playerId))).write( await (update(playerTable)..where((p) => p.id.equals(playerId))).write(
PlayerTableCompanion(name: Value(name)), PlayerTableCompanion(name: Value(name)),
); );
// Update name count for the new name
final count = await calculateNameCount(name: name);
if (count > 0) { if (count > 0) {
await (update(playerTable)..where((p) => p.name.equals(name))).write( await (update(playerTable)..where((p) => p.name.equals(name))).write(
PlayerTableCompanion(nameCount: Value(count)), PlayerTableCompanion(nameCount: Value(count)),
@@ -226,10 +229,10 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
/* Name count management */ /* Name count management */
/// Retrieves the count of players with the given [name]. /// Retrieves the count of players with the given [name].
Future<int> getNameCount({required String name}) async { Future<int?> getNameCount({required String name}) async {
final query = select(playerTable)..where((p) => p.name.equals(name)); final query = select(playerTable)..where((p) => p.name.equals(name));
final result = await query.get(); final result = await query.get();
return result.length; return result.isEmpty ? null : result.length;
} }
/// Updates the nameCount for the player with the given [playerId] to [nameCount]. /// Updates the nameCount for the player with the given [playerId] to [nameCount].
@@ -269,20 +272,19 @@ class PlayerDao extends DatabaseAccessor<AppDatabase> with _$PlayerDaoMixin {
final count = await getNameCount(name: name); final count = await getNameCount(name: name);
final int nameCount; final int nameCount;
if (count == 1) { if (count == null) {
// If no other players exist with the same name, set nameCount to 0
nameCount = 0;
} else if (count == 0) {
// If one other player exists with the same name, initialize the nameCount // If one other player exists with the same name, initialize the nameCount
await initializeNameCount(name: name); await initializeNameCount(name: name);
// And for the new player, set nameCount to 2 // And for the new player, set nameCount to 2
nameCount = 2; nameCount = 2;
} else if (count > 1) { } else {
// If more than one player exists with the same name, just increment // If more than one player exists with the same name, just increment
// the nameCount for the new player // the nameCount for the new player
nameCount = count + 1; nameCount = count + 1;
} else {
// If no other players exist with the same name, set nameCount to 0
nameCount = 0;
} }
return nameCount; return nameCount;
} }

View File

@@ -77,6 +77,7 @@ class _GroupViewState extends State<GroupView> {
); );
} }
return GroupTile( return GroupTile(
onPlayerChanged: loadGroups,
group: groups[index], group: groups[index],
onTap: () async { onTap: () async {
await Navigator.push( await Navigator.push(

View File

@@ -97,6 +97,7 @@ class _MatchViewState extends State<MatchView> {
child: Padding( child: Padding(
padding: const EdgeInsets.only(bottom: 12.0), padding: const EdgeInsets.only(bottom: 12.0),
child: MatchTile( child: MatchTile(
onPlayerEdited: loadMatches,
width: MediaQuery.sizeOf(context).width * 0.95, width: MediaQuery.sizeOf(context).width * 0.95,
onTap: () async { onTap: () async {
Navigator.push( Navigator.push(

View File

@@ -16,6 +16,7 @@ import 'package:tallee/presentation/widgets/buttons/main_menu_button.dart';
import 'package:tallee/presentation/widgets/colored_icon_container.dart'; import 'package:tallee/presentation/widgets/colored_icon_container.dart';
import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart'; import 'package:tallee/presentation/widgets/dialog/custom_alert_dialog.dart';
import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart'; import 'package:tallee/presentation/widgets/dialog/custom_dialog_action.dart';
import 'package:tallee/presentation/widgets/text_input/text_input_field.dart';
import 'package:tallee/presentation/widgets/tiles/info_tile.dart'; import 'package:tallee/presentation/widgets/tiles/info_tile.dart';
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart'; import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
@@ -37,7 +38,8 @@ class PlayerDetailView extends StatefulWidget {
class _PlayerDetailViewState extends State<PlayerDetailView> { class _PlayerDetailViewState extends State<PlayerDetailView> {
late final AppDatabase db; late final AppDatabase db;
late Player _player;
late String playerNameCount;
bool isLoading = true; bool isLoading = true;
/// Total matches played by this player /// Total matches played by this player
@@ -68,6 +70,7 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_player = widget.player;
db = Provider.of<AppDatabase>(context, listen: false); db = Provider.of<AppDatabase>(context, listen: false);
_loadData(); _loadData();
} }
@@ -75,6 +78,7 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final loc = AppLocalizations.of(context); final loc = AppLocalizations.of(context);
playerNameCount = getNameCountText(_player);
return Scaffold( return Scaffold(
appBar: AppBar( appBar: AppBar(
@@ -132,8 +136,11 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
), ),
), ),
const SizedBox(height: 10), const SizedBox(height: 10),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text( Text(
widget.player.name + getNameCountText(widget.player), _player.name,
style: const TextStyle( style: const TextStyle(
fontSize: 28, fontSize: 28,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
@@ -141,9 +148,20 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
Text(
playerNameCount,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: CustomTheme.textColor.withAlpha(120),
),
textAlign: TextAlign.center,
),
],
),
const SizedBox(height: 5), const SizedBox(height: 5),
Text( Text(
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.player.createdAt)}', '${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(_player.createdAt)}',
style: const TextStyle( style: const TextStyle(
fontSize: 12, fontSize: 12,
color: CustomTheme.textColor, color: CustomTheme.textColor,
@@ -152,8 +170,8 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
), ),
const SizedBox(height: 20), const SizedBox(height: 20),
InfoTile( InfoTile(
title: "Matches played in (${totalMatches})", title: "Matches part of (${totalMatches})",
icon: Icons.people, icon: Icons.sports_esports,
horizontalAlignment: CrossAxisAlignment.start, horizontalAlignment: CrossAxisAlignment.start,
content: Wrap( content: Wrap(
alignment: WrapAlignment.start, alignment: WrapAlignment.start,
@@ -209,8 +227,48 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
text: "Edit player", text: "Edit player",
icon: Icons.edit, icon: Icons.edit,
onPressed: () async { onPressed: () async {
//TODO: update player name in popup final controller = TextEditingController(text: _player.name);
widget.callback(); showDialog<bool>(
context: context,
builder: (context) => CustomAlertDialog(
title: "Change Name",
content: TextInputField(
controller: controller,
hintText: 'Set a player name',
),
actions: [
CustomDialogAction(
onPressed: () => Navigator.of(context).pop(true),
text: "Confirm",
),
CustomDialogAction(
onPressed: () => Navigator.of(context).pop(false),
buttonType: ButtonType.secondary,
text: loc.cancel,
),
],
),
).then((confirmed) async {
if (confirmed! && context.mounted) {
if (controller.text != _player.name) {
await db.playerDao.updatePlayerName(
playerId: _player.id,
name: controller.text,
);
widget.callback.call();
setState(() {
_player = Player(
name: controller.text,
createdAt: _player.createdAt,
id: _player.id,
nameCount: _player.nameCount,
description: _player.description,
);
playerNameCount = getNameCountText(_player);
});
}
}
});
}, },
), ),
), ),
@@ -224,17 +282,19 @@ class _PlayerDetailViewState extends State<PlayerDetailView> {
Future<void> _loadData() async { Future<void> _loadData() async {
isLoading = true; isLoading = true;
final fetchedMatches = await db.matchDao.getMatchesByPlayer( final fetchedMatches = await db.matchDao.getMatchesByPlayer(
playerId: widget.player.id, playerId: _player.id,
); );
final fetchedGroups = await db.groupDao.getGroupsByPlayer( final fetchedGroups = await db.groupDao.getGroupsByPlayer(
playerId: widget.player.id, playerId: _player.id,
); );
if (!mounted) return;
setState(() { setState(() {
playerMatches = fetchedMatches; playerMatches = fetchedMatches;
totalMatches = fetchedMatches.length; totalMatches = fetchedMatches.length;
matchesWon = fetchedMatches matchesWon = fetchedMatches
.where((match) => match.mvp.any((mvp) => mvp.id == widget.player.id)) .where((match) => match.mvp.any((mvp) => mvp.id == _player.id))
.length; .length;
playerGroups = fetchedGroups; playerGroups = fetchedGroups;
totalGroups = fetchedGroups.length; totalGroups = fetchedGroups.length;

View File

@@ -17,6 +17,7 @@ class GroupTile extends StatefulWidget {
required this.group, required this.group,
this.isHighlighted = false, this.isHighlighted = false,
this.onTap, this.onTap,
this.onPlayerChanged,
}); });
/// The group data to be displayed. /// The group data to be displayed.
@@ -28,6 +29,9 @@ class GroupTile extends StatefulWidget {
/// Callback function to be executed when the tile is tapped. /// Callback function to be executed when the tile is tapped.
final VoidCallback? onTap; final VoidCallback? onTap;
/// Callback function to be executed when the players in the group are changed.
final VoidCallback? onPlayerChanged;
@override @override
State<GroupTile> createState() => _GroupTileState(); State<GroupTile> createState() => _GroupTileState();
} }
@@ -101,7 +105,7 @@ class _GroupTileState extends State<GroupTile> {
builder: (context) => PlayerDetailView( builder: (context) => PlayerDetailView(
player: member, player: member,
callback: () { callback: () {
//TODO: implement callback widget.onPlayerChanged?.call();
}, },
), ),
), ),

View File

@@ -26,6 +26,7 @@ class MatchTile extends StatefulWidget {
required this.onTap, required this.onTap,
this.width, this.width,
this.compact = false, this.compact = false,
this.onPlayerEdited,
}); });
/// The match data to be displayed. /// The match data to be displayed.
@@ -34,6 +35,9 @@ class MatchTile extends StatefulWidget {
/// The callback invoked when the tile is tapped. /// The callback invoked when the tile is tapped.
final VoidCallback onTap; final VoidCallback onTap;
/// The callback invoked when the players are edited
final VoidCallback? onPlayerEdited;
/// Optional width for the tile. /// Optional width for the tile.
final double? width; final double? width;
@@ -233,7 +237,7 @@ class _MatchTileState extends State<MatchTile> {
builder: (context) => PlayerDetailView( builder: (context) => PlayerDetailView(
player: player, player: player,
callback: () { callback: () {
//TODO: implement callback widget.onPlayerEdited?.call();
}, },
), ),
), ),