implement basic player_detail_view.dart
This commit is contained in:
@@ -185,6 +185,38 @@ class GroupDao extends DatabaseAccessor<AppDatabase> with _$GroupDaoMixin {
|
|||||||
return count ?? 0;
|
return count ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves all groups a specific player belongs to.
|
||||||
|
/// Returns an empty list if the player is not part of any group.
|
||||||
|
Future<List<Group>> getGroupsByPlayer({required String playerId}) async {
|
||||||
|
final playerGroups = await (select(
|
||||||
|
playerGroupTable,
|
||||||
|
)..where((pg) => pg.playerId.equals(playerId))).get();
|
||||||
|
|
||||||
|
if (playerGroups.isEmpty) return [];
|
||||||
|
|
||||||
|
final groupIds = playerGroups.map((pg) => pg.groupId).toSet().toList();
|
||||||
|
final rows =
|
||||||
|
await (select(groupTable)
|
||||||
|
..where((g) => g.id.isIn(groupIds))
|
||||||
|
..orderBy([(g) => OrderingTerm.desc(g.createdAt)]))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return Future.wait(
|
||||||
|
rows.map((groupData) async {
|
||||||
|
final members = await db.playerGroupDao.getPlayersOfGroup(
|
||||||
|
groupId: groupData.id,
|
||||||
|
);
|
||||||
|
return Group(
|
||||||
|
id: groupData.id,
|
||||||
|
name: groupData.name,
|
||||||
|
description: groupData.description,
|
||||||
|
members: members,
|
||||||
|
createdAt: groupData.createdAt,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Checks if a group with the given [groupId] exists in the database.
|
/// Checks if a group with the given [groupId] exists in the database.
|
||||||
/// Returns `true` if the group exists, `false` otherwise.
|
/// Returns `true` if the group exists, `false` otherwise.
|
||||||
Future<bool> groupExists({required String groupId}) async {
|
Future<bool> groupExists({required String groupId}) async {
|
||||||
|
|||||||
@@ -352,6 +352,53 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
return count ?? 0;
|
return count ?? 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<List<Match>> getMatchesByPlayer({required String playerId}) async {
|
||||||
|
final playerMatches = await (select(
|
||||||
|
playerMatchTable,
|
||||||
|
)..where((pm) => pm.playerId.equals(playerId))).get();
|
||||||
|
|
||||||
|
if (playerMatches.isEmpty) return [];
|
||||||
|
|
||||||
|
final matchIds = playerMatches.map((pm) => pm.matchId).toSet().toList();
|
||||||
|
final rows =
|
||||||
|
await (select(matchTable)
|
||||||
|
..where((m) => m.id.isIn(matchIds))
|
||||||
|
..orderBy([(m) => OrderingTerm.desc(m.createdAt)]))
|
||||||
|
.get();
|
||||||
|
|
||||||
|
return Future.wait(
|
||||||
|
rows.map((row) async {
|
||||||
|
final game = await db.gameDao.getGameById(gameId: row.gameId);
|
||||||
|
|
||||||
|
Group? group;
|
||||||
|
if (row.groupId != null) {
|
||||||
|
group = await db.groupDao.getGroupById(groupId: row.groupId!);
|
||||||
|
}
|
||||||
|
|
||||||
|
final players = await db.playerMatchDao.getPlayersOfMatch(
|
||||||
|
matchId: row.id,
|
||||||
|
);
|
||||||
|
final scores = await db.scoreEntryDao.getAllMatchScores(
|
||||||
|
matchId: row.id,
|
||||||
|
);
|
||||||
|
final teams = await _getMatchTeams(matchId: row.id);
|
||||||
|
|
||||||
|
return Match(
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
game: game,
|
||||||
|
group: group,
|
||||||
|
players: players,
|
||||||
|
teams: teams.isEmpty ? null : teams,
|
||||||
|
notes: row.notes,
|
||||||
|
createdAt: row.createdAt,
|
||||||
|
endedAt: row.endedAt,
|
||||||
|
scores: scores,
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
/// Retrieves all matches associated with the given [groupId].
|
/// Retrieves all matches associated with the given [groupId].
|
||||||
/// Queries the database directly, filtering by [groupId].
|
/// Queries the database directly, filtering by [groupId].
|
||||||
Future<List<Match>> getMatchesByGroup({required String groupId}) async {
|
Future<List<Match>> getMatchesByGroup({required String groupId}) async {
|
||||||
|
|||||||
273
lib/presentation/views/main_menu/player_detail_view.dart
Normal file
273
lib/presentation/views/main_menu/player_detail_view.dart
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:provider/provider.dart';
|
||||||
|
import 'package:tallee/core/common.dart';
|
||||||
|
import 'package:tallee/core/custom_theme.dart';
|
||||||
|
import 'package:tallee/core/enums.dart';
|
||||||
|
import 'package:tallee/data/db/database.dart';
|
||||||
|
import 'package:tallee/data/models/game.dart';
|
||||||
|
import 'package:tallee/data/models/group.dart';
|
||||||
|
import 'package:tallee/data/models/match.dart';
|
||||||
|
import 'package:tallee/data/models/player.dart';
|
||||||
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/app_skeleton.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/buttons/haptic_icon_button.dart';
|
||||||
|
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/tiles/info_tile.dart';
|
||||||
|
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
|
|
||||||
|
class PlayerDetailView extends StatefulWidget {
|
||||||
|
const PlayerDetailView({
|
||||||
|
super.key,
|
||||||
|
required this.player,
|
||||||
|
required this.callback,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// The player to display
|
||||||
|
final Player player;
|
||||||
|
|
||||||
|
final VoidCallback callback;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<PlayerDetailView> createState() => _PlayerDetailViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _PlayerDetailViewState extends State<PlayerDetailView> {
|
||||||
|
late final AppDatabase db;
|
||||||
|
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
|
/// Total matches played by this player
|
||||||
|
int totalMatches = 0;
|
||||||
|
|
||||||
|
/// Total matches won by this player
|
||||||
|
int matchesWon = 0;
|
||||||
|
|
||||||
|
/// Total groups this player belongs to
|
||||||
|
int totalGroups = 0;
|
||||||
|
|
||||||
|
/// Full list of groups this player belongs to
|
||||||
|
List<Group> playerGroups = List.filled(
|
||||||
|
4,
|
||||||
|
Group(name: "Skeleton group", members: []),
|
||||||
|
);
|
||||||
|
|
||||||
|
/// Full list of matches this player played in
|
||||||
|
List<Match> playerMatches = List.filled(
|
||||||
|
4,
|
||||||
|
Match(
|
||||||
|
name: 'Skeleton match',
|
||||||
|
game: Game(name: 'Game name', ruleset: Ruleset.singleWinner),
|
||||||
|
players: [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
_loadData();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final loc = AppLocalizations.of(context);
|
||||||
|
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: const Text('Player Profile'),
|
||||||
|
actions: [
|
||||||
|
HapticIconButton(
|
||||||
|
icon: const Icon(Icons.delete),
|
||||||
|
onPressed: () async {
|
||||||
|
showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => CustomAlertDialog(
|
||||||
|
title: 'Delete player?',
|
||||||
|
content: Text(loc.this_cannot_be_undone),
|
||||||
|
actions: [
|
||||||
|
CustomDialogAction(
|
||||||
|
onPressed: () => Navigator.of(context).pop(true),
|
||||||
|
text: loc.delete,
|
||||||
|
),
|
||||||
|
CustomDialogAction(
|
||||||
|
onPressed: () => Navigator.of(context).pop(false),
|
||||||
|
buttonType: ButtonType.secondary,
|
||||||
|
text: loc.cancel,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).then((confirmed) async {
|
||||||
|
if (confirmed! && context.mounted) {
|
||||||
|
//TODO: implement player deletion in db
|
||||||
|
if (!context.mounted) return;
|
||||||
|
Navigator.pop(context);
|
||||||
|
widget.callback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: SafeArea(
|
||||||
|
child: Stack(
|
||||||
|
alignment: Alignment.center,
|
||||||
|
children: [
|
||||||
|
ListView(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
top: 20,
|
||||||
|
bottom: 100,
|
||||||
|
),
|
||||||
|
children: [
|
||||||
|
const Center(
|
||||||
|
child: ColoredIconContainer(
|
||||||
|
icon: Icons.person,
|
||||||
|
containerSize: 55,
|
||||||
|
iconSize: 38,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
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,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
Text(
|
||||||
|
'${loc.created_on} ${DateFormat.yMMMd(Localizations.localeOf(context).toString()).format(widget.player.createdAt)}',
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 12,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
InfoTile(
|
||||||
|
title: "Matches played in (${totalMatches})",
|
||||||
|
icon: Icons.people,
|
||||||
|
horizontalAlignment: CrossAxisAlignment.start,
|
||||||
|
content: Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
spacing: 12,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: playerMatches.map((match) {
|
||||||
|
return TextIconTile(text: match.name, iconEnabled: false);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
InfoTile(
|
||||||
|
title: "Groups part of (${totalGroups})",
|
||||||
|
icon: Icons.people,
|
||||||
|
horizontalAlignment: CrossAxisAlignment.start,
|
||||||
|
content: Wrap(
|
||||||
|
alignment: WrapAlignment.start,
|
||||||
|
crossAxisAlignment: WrapCrossAlignment.start,
|
||||||
|
spacing: 12,
|
||||||
|
runSpacing: 8,
|
||||||
|
children: playerGroups.map((group) {
|
||||||
|
return TextIconTile(text: group.name, iconEnabled: false);
|
||||||
|
}).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 15),
|
||||||
|
InfoTile(
|
||||||
|
title: loc.statistics,
|
||||||
|
icon: Icons.bar_chart,
|
||||||
|
content: AppSkeleton(
|
||||||
|
enabled: isLoading,
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
_buildStatRow(
|
||||||
|
"Matches played",
|
||||||
|
totalMatches.toString(),
|
||||||
|
),
|
||||||
|
_buildStatRow("Matches won", matchesWon.toString()),
|
||||||
|
_buildStatRow(
|
||||||
|
"Winrate",
|
||||||
|
'${totalMatches == 0 ? 0 : ((matchesWon / totalMatches) * 100).round()}%',
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
|
child: MainMenuButton(
|
||||||
|
text: "Edit player",
|
||||||
|
icon: Icons.edit,
|
||||||
|
onPressed: () async {
|
||||||
|
//TODO: update player name in popup
|
||||||
|
widget.callback();
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads statistics for this player
|
||||||
|
Future<void> _loadData() async {
|
||||||
|
isLoading = true;
|
||||||
|
final fetchedMatches = await db.matchDao.getMatchesByPlayer(
|
||||||
|
playerId: widget.player.id,
|
||||||
|
);
|
||||||
|
final fetchedGroups = await db.groupDao.getGroupsByPlayer(
|
||||||
|
playerId: widget.player.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
playerMatches = fetchedMatches;
|
||||||
|
totalMatches = fetchedMatches.length;
|
||||||
|
matchesWon = fetchedMatches
|
||||||
|
.where((match) => match.mvp.any((mvp) => mvp.id == widget.player.id))
|
||||||
|
.length;
|
||||||
|
playerGroups = fetchedGroups;
|
||||||
|
totalGroups = fetchedGroups.length;
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Builds a single statistic row with a label and value
|
||||||
|
/// - [label]: The label of the statistic
|
||||||
|
/// - [value]: The value of the statistic
|
||||||
|
Widget _buildStatRow(String label, String value) {
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 8),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
label,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: CustomTheme.textColor,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
value,
|
||||||
|
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.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/models/group.dart';
|
import 'package:tallee/data/models/group.dart';
|
||||||
|
import 'package:tallee/presentation/views/main_menu/player_detail_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
|
|
||||||
class GroupTile extends StatefulWidget {
|
class GroupTile extends StatefulWidget {
|
||||||
@@ -92,6 +94,19 @@ class _GroupTileState extends State<GroupTile> {
|
|||||||
text: member.name,
|
text: member.name,
|
||||||
suffixText: getNameCountText(member),
|
suffixText: getNameCountText(member),
|
||||||
iconEnabled: false,
|
iconEnabled: false,
|
||||||
|
onTileTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
adaptivePageRoute(
|
||||||
|
builder: (context) => PlayerDetailView(
|
||||||
|
player: member,
|
||||||
|
callback: () {
|
||||||
|
//TODO: implement callback
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ import 'dart:core' hide Match;
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.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/core/enums.dart';
|
import 'package:tallee/core/enums.dart';
|
||||||
import 'package:tallee/data/models/match.dart';
|
import 'package:tallee/data/models/match.dart';
|
||||||
import 'package:tallee/l10n/generated/app_localizations.dart';
|
import 'package:tallee/l10n/generated/app_localizations.dart';
|
||||||
|
import 'package:tallee/presentation/views/main_menu/player_detail_view.dart';
|
||||||
import 'package:tallee/presentation/widgets/game_label.dart';
|
import 'package:tallee/presentation/widgets/game_label.dart';
|
||||||
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
import 'package:tallee/presentation/widgets/tiles/text_icon_tile.dart';
|
||||||
|
|
||||||
@@ -224,6 +226,19 @@ class _MatchTileState extends State<MatchTile> {
|
|||||||
text: player.name,
|
text: player.name,
|
||||||
suffixText: getNameCountText(player),
|
suffixText: getNameCountText(player),
|
||||||
iconEnabled: false,
|
iconEnabled: false,
|
||||||
|
onTileTap: () {
|
||||||
|
Navigator.push(
|
||||||
|
context,
|
||||||
|
adaptivePageRoute(
|
||||||
|
builder: (context) => PlayerDetailView(
|
||||||
|
player: player,
|
||||||
|
callback: () {
|
||||||
|
//TODO: implement callback
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}).toList(),
|
}).toList(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ class TextIconTile extends StatelessWidget {
|
|||||||
this.suffixText = '',
|
this.suffixText = '',
|
||||||
this.iconEnabled = true,
|
this.iconEnabled = true,
|
||||||
this.onIconTap,
|
this.onIconTap,
|
||||||
|
this.onTileTap,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// The text to display in the tile.
|
/// The text to display in the tile.
|
||||||
@@ -25,52 +26,58 @@ class TextIconTile extends StatelessWidget {
|
|||||||
/// The callback to be invoked when the icon is tapped.
|
/// The callback to be invoked when the icon is tapped.
|
||||||
final VoidCallback? onIconTap;
|
final VoidCallback? onIconTap;
|
||||||
|
|
||||||
|
/// The callback to be invoked when the tile is tapped.
|
||||||
|
final VoidCallback? onTileTap;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return GestureDetector(
|
||||||
padding: const EdgeInsets.all(5),
|
onTap: onTileTap,
|
||||||
decoration: BoxDecoration(
|
child: Container(
|
||||||
color: CustomTheme.onBoxColor,
|
padding: const EdgeInsets.all(5),
|
||||||
borderRadius: BorderRadius.circular(12),
|
decoration: BoxDecoration(
|
||||||
),
|
color: CustomTheme.onBoxColor,
|
||||||
child: Row(
|
borderRadius: BorderRadius.circular(12),
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
),
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Row(
|
||||||
children: [
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
if (iconEnabled) const SizedBox(width: 3),
|
mainAxisSize: MainAxisSize.min,
|
||||||
Flexible(
|
children: [
|
||||||
child: RichText(
|
if (iconEnabled) const SizedBox(width: 3),
|
||||||
overflow: TextOverflow.ellipsis,
|
Flexible(
|
||||||
text: TextSpan(
|
child: RichText(
|
||||||
style: DefaultTextStyle.of(context).style,
|
overflow: TextOverflow.ellipsis,
|
||||||
children: [
|
text: TextSpan(
|
||||||
TextSpan(
|
style: DefaultTextStyle.of(context).style,
|
||||||
text: text,
|
children: [
|
||||||
style: const TextStyle(
|
TextSpan(
|
||||||
fontSize: 14,
|
text: text,
|
||||||
fontWeight: FontWeight.w500,
|
style: const TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
TextSpan(
|
||||||
TextSpan(
|
text: suffixText,
|
||||||
text: suffixText,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
fontSize: 13,
|
||||||
fontSize: 13,
|
fontWeight: FontWeight.w500,
|
||||||
fontWeight: FontWeight.w500,
|
color: CustomTheme.textColor.withAlpha(120),
|
||||||
color: CustomTheme.textColor.withAlpha(120),
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (iconEnabled) ...<Widget>[
|
||||||
if (iconEnabled) ...<Widget>[
|
const SizedBox(width: 3),
|
||||||
const SizedBox(width: 3),
|
GestureDetector(
|
||||||
GestureDetector(
|
onTap: onIconTap,
|
||||||
onTap: onIconTap,
|
child: const Icon(Icons.close, size: 20),
|
||||||
child: const Icon(Icons.close, size: 20),
|
),
|
||||||
),
|
],
|
||||||
],
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,6 +194,31 @@ void main() {
|
|||||||
expect(allGroups, isEmpty);
|
expect(allGroups, isEmpty);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getGroupsByPlayer() works correctly', () async {
|
||||||
|
await database.groupDao.addGroupsAsList(
|
||||||
|
groups: [testGroup1, testGroup2],
|
||||||
|
);
|
||||||
|
|
||||||
|
final groups = await database.groupDao.getGroupsByPlayer(
|
||||||
|
playerId: testPlayer2.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(groups, hasLength(2));
|
||||||
|
expect(groups.any((group) => group.id == testGroup1.id), isTrue);
|
||||||
|
expect(groups.any((group) => group.id == testGroup2.id), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'getGroupsByPlayer() returns empty list for non-existent player',
|
||||||
|
() async {
|
||||||
|
final groups = await database.groupDao.getGroupsByPlayer(
|
||||||
|
playerId: 'non-existent-player-id',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(groups, isEmpty);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test('addGroupsAsList() with duplicate groups only adds once', () async {
|
test('addGroupsAsList() with duplicate groups only adds once', () async {
|
||||||
await database.groupDao.addGroupsAsList(
|
await database.groupDao.addGroupsAsList(
|
||||||
groups: [testGroup1, testGroup1, testGroup1],
|
groups: [testGroup1, testGroup1, testGroup1],
|
||||||
|
|||||||
@@ -260,6 +260,34 @@ void main() {
|
|||||||
expect(match.group!.id, testGroup1.id);
|
expect(match.group!.id, testGroup1.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getMatchesByPlayer() works correctly', () async {
|
||||||
|
await database.matchDao.addMatchesAsList(
|
||||||
|
matches: [testMatch1, testMatch2],
|
||||||
|
);
|
||||||
|
|
||||||
|
final matches = await database.matchDao.getMatchesByPlayer(
|
||||||
|
playerId: testPlayer1.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(matches, hasLength(1));
|
||||||
|
expect(matches.first.id, testMatch2.id);
|
||||||
|
expect(
|
||||||
|
matches.first.players.any((p) => p.id == testPlayer1.id),
|
||||||
|
isTrue,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'getMatchesByPlayer() returns empty list for non-existent player',
|
||||||
|
() async {
|
||||||
|
final matches = await database.matchDao.getMatchesByPlayer(
|
||||||
|
playerId: 'non-existing-player-id',
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(matches, isEmpty);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
test('getMatchCount() works correctly', () async {
|
test('getMatchCount() works correctly', () async {
|
||||||
var count = await database.matchDao.getMatchCount();
|
var count = await database.matchDao.getMatchCount();
|
||||||
expect(count, 0);
|
expect(count, 0);
|
||||||
|
|||||||
Reference in New Issue
Block a user