From 869c70ff6317f168edd108078753a0a0bf0f370c Mon Sep 17 00:00:00 2001 From: Mathis Kirchner Date: Wed, 20 May 2026 19:50:34 +0200 Subject: [PATCH] add player change callbacks and improve player detail view --- lib/data/dao/player_dao.dart | 24 ++--- .../main_menu/group_view/group_view.dart | 1 + .../main_menu/match_view/match_view.dart | 1 + .../views/main_menu/player_detail_view.dart | 94 +++++++++++++++---- .../widgets/tiles/group_tile.dart | 6 +- .../widgets/tiles/match_tile.dart | 6 +- 6 files changed, 102 insertions(+), 30 deletions(-) diff --git a/lib/data/dao/player_dao.dart b/lib/data/dao/player_dao.dart index 51e5845..71fbe6c 100644 --- a/lib/data/dao/player_dao.dart +++ b/lib/data/dao/player_dao.dart @@ -169,15 +169,18 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { .map((row) => row.name) .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 = await (update(playerTable)..where((p) => p.id.equals(playerId))).write( PlayerTableCompanion(name: Value(name)), ); - // Update name count for the new name - final count = await calculateNameCount(name: name); if (count > 0) { await (update(playerTable)..where((p) => p.name.equals(name))).write( PlayerTableCompanion(nameCount: Value(count)), @@ -226,10 +229,10 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { /* Name count management */ /// Retrieves the count of players with the given [name]. - Future getNameCount({required String name}) async { + Future getNameCount({required String name}) async { final query = select(playerTable)..where((p) => p.name.equals(name)); 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]. @@ -269,20 +272,19 @@ class PlayerDao extends DatabaseAccessor with _$PlayerDaoMixin { final count = await getNameCount(name: name); 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 await initializeNameCount(name: name); // And for the new player, set nameCount to 2 nameCount = 2; - } else if (count > 1) { + } else { // If more than one player exists with the same name, just increment // the nameCount for the new player nameCount = count + 1; - } else { - // If no other players exist with the same name, set nameCount to 0 - nameCount = 0; } - return nameCount; } diff --git a/lib/presentation/views/main_menu/group_view/group_view.dart b/lib/presentation/views/main_menu/group_view/group_view.dart index c8a9398..50bc6e8 100644 --- a/lib/presentation/views/main_menu/group_view/group_view.dart +++ b/lib/presentation/views/main_menu/group_view/group_view.dart @@ -77,6 +77,7 @@ class _GroupViewState extends State { ); } return GroupTile( + onPlayerChanged: loadGroups, group: groups[index], onTap: () async { await Navigator.push( diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index a7f60c6..83ff069 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -97,6 +97,7 @@ class _MatchViewState extends State { child: Padding( padding: const EdgeInsets.only(bottom: 12.0), child: MatchTile( + onPlayerEdited: loadMatches, width: MediaQuery.sizeOf(context).width * 0.95, onTap: () async { Navigator.push( diff --git a/lib/presentation/views/main_menu/player_detail_view.dart b/lib/presentation/views/main_menu/player_detail_view.dart index a946133..75c2ff6 100644 --- a/lib/presentation/views/main_menu/player_detail_view.dart +++ b/lib/presentation/views/main_menu/player_detail_view.dart @@ -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/dialog/custom_alert_dialog.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/text_icon_tile.dart'; @@ -37,7 +38,8 @@ class PlayerDetailView extends StatefulWidget { class _PlayerDetailViewState extends State { late final AppDatabase db; - + late Player _player; + late String playerNameCount; bool isLoading = true; /// Total matches played by this player @@ -68,6 +70,7 @@ class _PlayerDetailViewState extends State { @override void initState() { super.initState(); + _player = widget.player; db = Provider.of(context, listen: false); _loadData(); } @@ -75,6 +78,7 @@ class _PlayerDetailViewState extends State { @override Widget build(BuildContext context) { final loc = AppLocalizations.of(context); + playerNameCount = getNameCountText(_player); return Scaffold( appBar: AppBar( @@ -132,18 +136,32 @@ class _PlayerDetailViewState extends State { ), ), const SizedBox(height: 10), - Text( - widget.player.name + getNameCountText(widget.player), - style: const TextStyle( - fontSize: 28, - fontWeight: FontWeight.bold, - color: CustomTheme.textColor, - ), - textAlign: TextAlign.center, + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + _player.name, + style: const TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor, + ), + textAlign: TextAlign.center, + ), + Text( + playerNameCount, + style: TextStyle( + fontSize: 28, + fontWeight: FontWeight.bold, + color: CustomTheme.textColor.withAlpha(120), + ), + textAlign: TextAlign.center, + ), + ], ), const SizedBox(height: 5), 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( fontSize: 12, color: CustomTheme.textColor, @@ -152,8 +170,8 @@ class _PlayerDetailViewState extends State { ), const SizedBox(height: 20), InfoTile( - title: "Matches played in (${totalMatches})", - icon: Icons.people, + title: "Matches part of (${totalMatches})", + icon: Icons.sports_esports, horizontalAlignment: CrossAxisAlignment.start, content: Wrap( alignment: WrapAlignment.start, @@ -209,8 +227,48 @@ class _PlayerDetailViewState extends State { text: "Edit player", icon: Icons.edit, onPressed: () async { - //TODO: update player name in popup - widget.callback(); + final controller = TextEditingController(text: _player.name); + showDialog( + 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 { Future _loadData() async { isLoading = true; final fetchedMatches = await db.matchDao.getMatchesByPlayer( - playerId: widget.player.id, + playerId: _player.id, ); final fetchedGroups = await db.groupDao.getGroupsByPlayer( - playerId: widget.player.id, + playerId: _player.id, ); + if (!mounted) return; + setState(() { playerMatches = fetchedMatches; totalMatches = fetchedMatches.length; matchesWon = fetchedMatches - .where((match) => match.mvp.any((mvp) => mvp.id == widget.player.id)) + .where((match) => match.mvp.any((mvp) => mvp.id == _player.id)) .length; playerGroups = fetchedGroups; totalGroups = fetchedGroups.length; diff --git a/lib/presentation/widgets/tiles/group_tile.dart b/lib/presentation/widgets/tiles/group_tile.dart index 6f22d39..c01dcbe 100644 --- a/lib/presentation/widgets/tiles/group_tile.dart +++ b/lib/presentation/widgets/tiles/group_tile.dart @@ -17,6 +17,7 @@ class GroupTile extends StatefulWidget { required this.group, this.isHighlighted = false, this.onTap, + this.onPlayerChanged, }); /// The group data to be displayed. @@ -28,6 +29,9 @@ class GroupTile extends StatefulWidget { /// Callback function to be executed when the tile is tapped. final VoidCallback? onTap; + /// Callback function to be executed when the players in the group are changed. + final VoidCallback? onPlayerChanged; + @override State createState() => _GroupTileState(); } @@ -101,7 +105,7 @@ class _GroupTileState extends State { builder: (context) => PlayerDetailView( player: member, callback: () { - //TODO: implement callback + widget.onPlayerChanged?.call(); }, ), ), diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index d9583e7..c3d0242 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -26,6 +26,7 @@ class MatchTile extends StatefulWidget { required this.onTap, this.width, this.compact = false, + this.onPlayerEdited, }); /// The match data to be displayed. @@ -34,6 +35,9 @@ class MatchTile extends StatefulWidget { /// The callback invoked when the tile is tapped. final VoidCallback onTap; + /// The callback invoked when the players are edited + final VoidCallback? onPlayerEdited; + /// Optional width for the tile. final double? width; @@ -233,7 +237,7 @@ class _MatchTileState extends State { builder: (context) => PlayerDetailView( player: player, callback: () { - //TODO: implement callback + widget.onPlayerEdited?.call(); }, ), ),