MVP #141
2
lib/core/constants.dart
Normal file
2
lib/core/constants.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/// Minimum duration of all app skeletons
|
||||||
|
Duration minimumSkeletonDuration = const Duration(milliseconds: 250);
|
||||||
@@ -21,7 +21,7 @@ class GroupMatchDao extends DatabaseAccessor<AppDatabase>
|
|||||||
}
|
}
|
||||||
await into(groupMatchTable).insert(
|
await into(groupMatchTable).insert(
|
||||||
GroupMatchTableCompanion.insert(groupId: groupId, matchId: matchId),
|
GroupMatchTableCompanion.insert(groupId: groupId, matchId: matchId),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrIgnore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,8 +65,9 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds a new [Match] to the database.
|
/// Adds a new [Match] to the database. Also adds players and group
|
||||||
/// Also adds associated players and group if they exist.
|
/// associations. This method assumes that the players and groups added to
|
||||||
|
/// this match are already present in the database.
|
||||||
Future<void> addMatch({required Match match}) async {
|
Future<void> addMatch({required Match match}) async {
|
||||||
await db.transaction(() async {
|
await db.transaction(() async {
|
||||||
await into(matchTable).insert(
|
await into(matchTable).insert(
|
||||||
@@ -80,7 +81,6 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (match.players != null) {
|
if (match.players != null) {
|
||||||
await db.playerDao.addPlayersAsList(players: match.players!);
|
|
||||||
for (final p in match.players ?? []) {
|
for (final p in match.players ?? []) {
|
||||||
await db.playerMatchDao.addPlayerToMatch(
|
await db.playerMatchDao.addPlayerToMatch(
|
||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
@@ -90,7 +90,6 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (match.group != null) {
|
if (match.group != null) {
|
||||||
await db.groupDao.addGroup(group: match.group!);
|
|
||||||
await db.groupMatchDao.addGroupToMatch(
|
await db.groupMatchDao.addGroupToMatch(
|
||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
groupId: match.group!.id,
|
groupId: match.group!.id,
|
||||||
@@ -102,6 +101,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
/// Adds multiple [Match]s to the database in a batch operation.
|
/// Adds multiple [Match]s to the database in a batch operation.
|
||||||
/// Also adds associated players and groups if they exist.
|
/// Also adds associated players and groups if they exist.
|
||||||
/// If the [matches] list is empty, the method returns immediately.
|
/// If the [matches] list is empty, the method returns immediately.
|
||||||
|
/// This Method should only be used to import matches from a different device.
|
||||||
Future<void> addMatchAsList({required List<Match> matches}) async {
|
Future<void> addMatchAsList({required List<Match> matches}) async {
|
||||||
if (matches.isEmpty) return;
|
if (matches.isEmpty) return;
|
||||||
await db.transaction(() async {
|
await db.transaction(() async {
|
||||||
@@ -186,7 +186,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
playerId: p.id,
|
playerId: p.id,
|
||||||
),
|
),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrIgnore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,7 +204,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
playerId: m.id,
|
playerId: m.id,
|
||||||
groupId: match.group!.id,
|
groupId: match.group!.id,
|
||||||
),
|
),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrIgnore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -221,7 +221,7 @@ class MatchDao extends DatabaseAccessor<AppDatabase> with _$MatchDaoMixin {
|
|||||||
matchId: match.id,
|
matchId: match.id,
|
||||||
groupId: match.group!.id,
|
groupId: match.group!.id,
|
||||||
),
|
),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrIgnore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
|
|||||||
}) async {
|
}) async {
|
||||||
await into(playerMatchTable).insert(
|
await into(playerMatchTable).insert(
|
||||||
PlayerMatchTableCompanion.insert(playerId: playerId, matchId: matchId),
|
PlayerMatchTableCompanion.insert(playerId: playerId, matchId: matchId),
|
||||||
mode: InsertMode.insertOrReplace,
|
mode: InsertMode.insertOrIgnore,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,7 +121,7 @@ class PlayerMatchDao extends DatabaseAccessor<AppDatabase>
|
|||||||
inserts.map(
|
inserts.map(
|
||||||
(c) => into(
|
(c) => into(
|
||||||
playerMatchTable,
|
playerMatchTable,
|
||||||
).insert(c, mode: InsertMode.insertOrReplace),
|
).insert(c, mode: InsertMode.insertOrIgnore),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:game_tracker/core/constants.dart';
|
||||||
import 'package:game_tracker/core/custom_theme.dart';
|
import 'package:game_tracker/core/custom_theme.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/group.dart';
|
import 'package:game_tracker/data/dto/group.dart';
|
||||||
@@ -18,15 +19,15 @@ class GroupsView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _GroupsViewState extends State<GroupsView> {
|
class _GroupsViewState extends State<GroupsView> {
|
||||||
late Future<List<Group>> _allGroupsFuture;
|
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
late List<Group> loadedGroups;
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
final player = Player(name: 'Skeleton Player');
|
List<Group> groups = List.filled(
|
||||||
late final List<Group> skeletonData = List.filled(
|
|
||||||
7,
|
7,
|
||||||
Group(
|
Group(
|
||||||
name: 'Skeleton Match',
|
name: 'Skeleton Group',
|
||||||
members: [player, player, player, player, player, player],
|
members: List.filled(6, Player(name: 'Skeleton Player')),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -34,10 +35,7 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
_allGroupsFuture = Future.delayed(
|
loadGroups();
|
||||||
const Duration(milliseconds: 250),
|
|
||||||
() => db.groupDao.getAllGroups(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -47,36 +45,17 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
body: Stack(
|
body: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<List<Group>>(
|
AppSkeleton(
|
||||||
future: _allGroupsFuture,
|
enabled: isLoading,
|
||||||
builder:
|
child: Visibility(
|
||||||
(BuildContext context, AsyncSnapshot<List<Group>> snapshot) {
|
visible: groups.isNotEmpty,
|
||||||
if (snapshot.hasError) {
|
replacement: const Center(
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.report,
|
|
||||||
title: 'Error',
|
|
||||||
message: 'Group data couldn\'t\nbe loaded',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
|
||||||
(!snapshot.hasData || snapshot.data!.isEmpty)) {
|
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
child: TopCenteredMessage(
|
||||||
icon: Icons.info,
|
icon: Icons.info,
|
||||||
title: 'Info',
|
title: 'Info',
|
||||||
message: 'No groups created yet',
|
message: 'No groups created yet',
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
|
||||||
final bool isLoading =
|
|
||||||
snapshot.connectionState == ConnectionState.waiting;
|
|
||||||
final List<Group> groups =
|
|
||||||
isLoading ? skeletonData : (snapshot.data ?? [])
|
|
||||||
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
|
||||||
return AppSkeleton(
|
|
||||||
enabled: isLoading,
|
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.only(bottom: 85),
|
padding: const EdgeInsets.only(bottom: 85),
|
||||||
itemCount: groups.length + 1,
|
itemCount: groups.length + 1,
|
||||||
@@ -89,8 +68,7 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
return GroupTile(group: groups[index]);
|
return GroupTile(group: groups[index]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom,
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
@@ -107,7 +85,7 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
setState(() {
|
setState(() {
|
||||||
_allGroupsFuture = db.groupDao.getAllGroups();
|
loadGroups();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -116,4 +94,22 @@ class _GroupsViewState extends State<GroupsView> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadGroups() {
|
||||||
|
Future.wait([
|
||||||
|
db.groupDao.getAllGroups(),
|
||||||
|
Future.delayed(minimumSkeletonDuration),
|
||||||
|
]).then((results) {
|
||||||
|
loadedGroups = results[0] as List<Group>;
|
||||||
|
setState(() {
|
||||||
|
groups = loadedGroups
|
||||||
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||||
|
});
|
||||||
|
if (mounted) {
|
||||||
|
setState(() {
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:game_tracker/core/constants.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/group.dart';
|
import 'package:game_tracker/data/dto/group.dart';
|
||||||
import 'package:game_tracker/data/dto/match.dart';
|
import 'package:game_tracker/data/dto/match.dart';
|
||||||
@@ -18,12 +19,11 @@ class HomeView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _HomeViewState extends State<HomeView> {
|
class _HomeViewState extends State<HomeView> {
|
||||||
late Future<int> _matchCountFuture;
|
|
||||||
late Future<int> _groupCountFuture;
|
|
||||||
late Future<List<Match>> _recentMatchesFuture;
|
|
||||||
bool isLoading = true;
|
bool isLoading = true;
|
||||||
|
int matchCount = 0;
|
||||||
late final List<Match> skeletonData = List.filled(
|
int groupCount = 0;
|
||||||
|
List<Match> loadedRecentMatches = [];
|
||||||
|
List<Match> recentMatches = List.filled(
|
||||||
2,
|
2,
|
||||||
Match(
|
Match(
|
||||||
name: 'Skeleton Match',
|
name: 'Skeleton Match',
|
||||||
@@ -39,19 +39,28 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
_matchCountFuture = db.matchDao.getMatchCount();
|
|
||||||
_groupCountFuture = db.groupDao.getGroupCount();
|
|
||||||
_recentMatchesFuture = db.matchDao.getAllMatches();
|
|
||||||
|
|
||||||
Future.wait([
|
Future.wait([
|
||||||
_matchCountFuture,
|
db.matchDao.getMatchCount(),
|
||||||
_groupCountFuture,
|
db.groupDao.getGroupCount(),
|
||||||
_recentMatchesFuture,
|
db.matchDao.getAllMatches(),
|
||||||
]).then((_) async {
|
Future.delayed(minimumSkeletonDuration),
|
||||||
await Future.delayed(const Duration(milliseconds: 250));
|
]).then((results) {
|
||||||
|
matchCount = results[0] as int;
|
||||||
|
groupCount = results[1] as int;
|
||||||
|
loadedRecentMatches = results[2] as List<Match>;
|
||||||
|
recentMatches =
|
||||||
|
(loadedRecentMatches
|
||||||
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
|
||||||
|
.take(2)
|
||||||
|
.toList();
|
||||||
|
if (loadedRecentMatches.length < 2) {
|
||||||
|
recentMatches.add(
|
||||||
|
Match(name: 'Dummy Match', winner: null, group: null, players: null),
|
||||||
|
);
|
||||||
|
}
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
@@ -65,6 +74,7 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
return LayoutBuilder(
|
return LayoutBuilder(
|
||||||
builder: (BuildContext context, BoxConstraints constraints) {
|
builder: (BuildContext context, BoxConstraints constraints) {
|
||||||
return AppSkeleton(
|
return AppSkeleton(
|
||||||
|
fixLayoutBuilder: true,
|
||||||
enabled: isLoading,
|
enabled: isLoading,
|
||||||
child: SingleChildScrollView(
|
child: SingleChildScrollView(
|
||||||
child: Column(
|
child: Column(
|
||||||
@@ -73,38 +83,20 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<int>(
|
QuickInfoTile(
|
||||||
future: _matchCountFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final int count = (snapshot.hasData)
|
|
||||||
? snapshot.data!
|
|
||||||
: 0;
|
|
||||||
return QuickInfoTile(
|
|
||||||
width: constraints.maxWidth * 0.45,
|
width: constraints.maxWidth * 0.45,
|
||||||
height: constraints.maxHeight * 0.15,
|
height: constraints.maxHeight * 0.15,
|
||||||
title: 'Matches',
|
title: 'Matches',
|
||||||
icon: Icons.groups_rounded,
|
icon: Icons.groups_rounded,
|
||||||
value: count,
|
value: matchCount,
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
SizedBox(width: constraints.maxWidth * 0.05),
|
SizedBox(width: constraints.maxWidth * 0.05),
|
||||||
FutureBuilder<int>(
|
QuickInfoTile(
|
||||||
future: _groupCountFuture,
|
|
||||||
builder: (context, snapshot) {
|
|
||||||
final int count =
|
|
||||||
(snapshot.connectionState == ConnectionState.done &&
|
|
||||||
snapshot.hasData)
|
|
||||||
? snapshot.data!
|
|
||||||
: 0;
|
|
||||||
return QuickInfoTile(
|
|
||||||
width: constraints.maxWidth * 0.45,
|
width: constraints.maxWidth * 0.45,
|
||||||
height: constraints.maxHeight * 0.15,
|
height: constraints.maxHeight * 0.15,
|
||||||
title: 'Groups',
|
title: 'Groups',
|
||||||
icon: Icons.groups_rounded,
|
icon: Icons.groups_rounded,
|
||||||
value: count,
|
value: groupCount,
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -116,80 +108,48 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
icon: Icons.timer,
|
icon: Icons.timer,
|
||||||
content: Padding(
|
content: Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
padding: const EdgeInsets.symmetric(horizontal: 40.0),
|
||||||
child: FutureBuilder(
|
child: Visibility(
|
||||||
future: _recentMatchesFuture,
|
visible: !isLoading && loadedRecentMatches.isNotEmpty,
|
||||||
builder:
|
replacement: const Center(
|
||||||
(
|
heightFactor: 12,
|
||||||
BuildContext context,
|
child: Text('No recent games available'),
|
||||||
AsyncSnapshot<List<Match>> snapshot,
|
|
||||||
) {
|
|
||||||
if (snapshot.hasError) {
|
|
||||||
return const Center(
|
|
||||||
heightFactor: 4,
|
|
||||||
child: Text(
|
|
||||||
'Error while loading recent matches.',
|
|
||||||
),
|
),
|
||||||
);
|
child: Column(
|
||||||
}
|
|
||||||
final List<Match> matches =
|
|
||||||
(isLoading
|
|
||||||
? skeletonData
|
|
||||||
: (snapshot.data ?? [])
|
|
||||||
..sort(
|
|
||||||
(a, b) => b.createdAt.compareTo(
|
|
||||||
a.createdAt,
|
|
||||||
),
|
|
||||||
))
|
|
||||||
.take(2)
|
|
||||||
.toList();
|
|
||||||
if (matches.isNotEmpty) {
|
|
||||||
return Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
MatchTile(
|
MatchTile(
|
||||||
matchTitle: matches[0].name,
|
matchTitle: recentMatches[0].name,
|
||||||
game: 'Winner',
|
game: 'Winner',
|
||||||
ruleset: 'Ruleset',
|
ruleset: 'Ruleset',
|
||||||
players: _getPlayerText(matches[0]),
|
players: _getPlayerText(recentMatches[0]),
|
||||||
winner: matches[0].winner == null
|
winner: recentMatches[0].winner == null
|
||||||
? 'Match in progress...'
|
? 'Match in progress...'
|
||||||
: matches[0].winner!.name,
|
: recentMatches[0].winner!.name,
|
||||||
),
|
),
|
||||||
const Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.symmetric(
|
padding: EdgeInsets.symmetric(vertical: 8.0),
|
||||||
vertical: 8.0,
|
|
||||||
),
|
|
||||||
child: Divider(),
|
child: Divider(),
|
||||||
),
|
),
|
||||||
if (matches.length > 1) ...[
|
if (loadedRecentMatches.length > 1) ...[
|
||||||
MatchTile(
|
MatchTile(
|
||||||
matchTitle: matches[1].name,
|
matchTitle: recentMatches[1].name,
|
||||||
game: 'Winner',
|
game: 'Winner',
|
||||||
ruleset: 'Ruleset',
|
ruleset: 'Ruleset',
|
||||||
players: _getPlayerText(matches[1]),
|
players: _getPlayerText(recentMatches[1]),
|
||||||
winner: matches[1].winner == null
|
winner: recentMatches[1].winner == null
|
||||||
? 'Game in progress...'
|
? 'Game in progress...'
|
||||||
: matches[1].winner!.name,
|
: recentMatches[1].winner!.name,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
] else ...[
|
] else ...[
|
||||||
const Center(
|
const Center(
|
||||||
heightFactor: 4,
|
heightFactor: 5.35,
|
||||||
child: Text(
|
child: Text('No second game available'),
|
||||||
'No second game available.',
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
);
|
),
|
||||||
} else {
|
|
||||||
return const Center(
|
|
||||||
heightFactor: 12,
|
|
||||||
child: Text('No recent games available.'),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -199,7 +159,6 @@ class _HomeViewState extends State<HomeView> {
|
|||||||
title: 'Quick Create',
|
title: 'Quick Create',
|
||||||
icon: Icons.add_box_rounded,
|
icon: Icons.add_box_rounded,
|
||||||
content: Column(
|
content: Column(
|
||||||
spacing: 8,
|
|
||||||
children: [
|
children: [
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
|||||||
@@ -74,11 +74,19 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
|
|||||||
Expanded(
|
Expanded(
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible: filteredGroups.isNotEmpty,
|
visible: filteredGroups.isNotEmpty,
|
||||||
|
replacement: Visibility(
|
||||||
|
visible: widget.groups.isNotEmpty,
|
||||||
replacement: const TopCenteredMessage(
|
replacement: const TopCenteredMessage(
|
||||||
|
icon: Icons.info,
|
||||||
|
title: 'Info',
|
||||||
|
message: 'You have no groups created yet',
|
||||||
|
),
|
||||||
|
child: const TopCenteredMessage(
|
||||||
icon: Icons.info,
|
icon: Icons.info,
|
||||||
title: 'Info',
|
title: 'Info',
|
||||||
message: 'There is no group matching your search',
|
message: 'There is no group matching your search',
|
||||||
),
|
),
|
||||||
|
),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.only(bottom: 85),
|
padding: const EdgeInsets.only(bottom: 85),
|
||||||
itemCount: filteredGroups.length,
|
itemCount: filteredGroups.length,
|
||||||
|
|||||||
@@ -28,12 +28,6 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
/// Reference to the app database
|
/// Reference to the app database
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
|
||||||
/// Futures to load all groups and players from the database
|
|
||||||
late Future<List<Group>> _allGroupsFuture;
|
|
||||||
|
|
||||||
/// Future to load all players from the database
|
|
||||||
late Future<List<Player>> _allPlayersFuture;
|
|
||||||
|
|
||||||
/// Controller for the game name input field
|
/// Controller for the game name input field
|
||||||
final TextEditingController _gameNameController = TextEditingController();
|
final TextEditingController _gameNameController = TextEditingController();
|
||||||
|
|
||||||
@@ -107,14 +101,13 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
|
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
|
||||||
_allGroupsFuture = db.groupDao.getAllGroups();
|
Future.wait([
|
||||||
_allPlayersFuture = db.playerDao.getAllPlayers();
|
db.groupDao.getAllGroups(),
|
||||||
|
db.playerDao.getAllPlayers(),
|
||||||
Future.wait([_allGroupsFuture, _allPlayersFuture]).then((result) async {
|
]).then((result) async {
|
||||||
groupsList = result[0] as List<Group>;
|
groupsList = result[0] as List<Group>;
|
||||||
playerList = result[1] as List<Player>;
|
playerList = result[1] as List<Player>;
|
||||||
});
|
});
|
||||||
|
|
||||||
filteredPlayerList = List.from(playerList);
|
filteredPlayerList = List.from(playerList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ class _GameResultViewState extends State<GameResultView> {
|
|||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
leading: IconButton(
|
||||||
|
icon: const Icon(Icons.close),
|
||||||
|
onPressed: () {
|
||||||
|
widget.onWinnerChanged?.call();
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
),
|
||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
title: Text(
|
title: Text(
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import 'dart:core' hide Match;
|
|||||||
|
|
||||||
import 'package:flutter/cupertino.dart';
|
import 'package:flutter/cupertino.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:game_tracker/core/constants.dart';
|
||||||
import 'package:game_tracker/core/custom_theme.dart';
|
import 'package:game_tracker/core/custom_theme.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/group.dart';
|
import 'package:game_tracker/data/dto/group.dart';
|
||||||
@@ -23,16 +24,16 @@ class MatchView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _MatchViewState extends State<MatchView> {
|
class _MatchViewState extends State<MatchView> {
|
||||||
late Future<List<Match>> _gameListFuture;
|
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
|
bool isLoading = true;
|
||||||
|
|
||||||
late final List<Match> skeletonData = List.filled(
|
List<Match> matches = List.filled(
|
||||||
4,
|
4,
|
||||||
Match(
|
Match(
|
||||||
name: 'Skeleton Gamename',
|
name: 'Skeleton Gamename',
|
||||||
group: Group(
|
group: Group(
|
||||||
name: 'Groupname',
|
name: 'Groupname',
|
||||||
members: List.generate(5, (index) => Player(name: 'Player')),
|
members: List.filled(5, Player(name: 'Player')),
|
||||||
),
|
),
|
||||||
winner: Player(name: 'Player'),
|
winner: Player(name: 'Player'),
|
||||||
players: [Player(name: 'Player')],
|
players: [Player(name: 'Player')],
|
||||||
@@ -43,10 +44,7 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
_gameListFuture = Future.delayed(
|
loadGames();
|
||||||
const Duration(milliseconds: 250),
|
|
||||||
() => db.matchDao.getAllMatches(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -56,39 +54,17 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
body: Stack(
|
body: Stack(
|
||||||
alignment: Alignment.center,
|
alignment: Alignment.center,
|
||||||
children: [
|
children: [
|
||||||
FutureBuilder<List<Match>>(
|
AppSkeleton(
|
||||||
future: _gameListFuture,
|
enabled: isLoading,
|
||||||
builder:
|
child: Visibility(
|
||||||
(BuildContext context, AsyncSnapshot<List<Match>> snapshot) {
|
visible: matches.isNotEmpty,
|
||||||
if (snapshot.hasError) {
|
replacement: const Center(
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.report,
|
|
||||||
title: 'Error',
|
|
||||||
message: 'Game data could not be loaded',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (snapshot.connectionState == ConnectionState.done &&
|
|
||||||
(!snapshot.hasData || snapshot.data!.isEmpty)) {
|
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
child: TopCenteredMessage(
|
||||||
icon: Icons.report,
|
icon: Icons.report,
|
||||||
title: 'Info',
|
title: 'Info',
|
||||||
message: 'No games created yet',
|
message: 'No games created yet',
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
}
|
|
||||||
final bool isLoading =
|
|
||||||
snapshot.connectionState == ConnectionState.waiting;
|
|
||||||
final List<Match> matches =
|
|
||||||
(isLoading ? skeletonData : (snapshot.data ?? [])
|
|
||||||
..sort(
|
|
||||||
(a, b) => b.createdAt.compareTo(a.createdAt),
|
|
||||||
))
|
|
||||||
.toList();
|
|
||||||
return AppSkeleton(
|
|
||||||
enabled: isLoading,
|
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
padding: const EdgeInsets.only(bottom: 85),
|
padding: const EdgeInsets.only(bottom: 85),
|
||||||
itemCount: matches.length + 1,
|
itemCount: matches.length + 1,
|
||||||
@@ -106,7 +82,7 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
fullscreenDialog: true,
|
fullscreenDialog: true,
|
||||||
builder: (context) => GameResultView(
|
builder: (context) => GameResultView(
|
||||||
match: matches[index],
|
match: matches[index],
|
||||||
onWinnerChanged: refreshGameList,
|
onWinnerChanged: loadGames,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -115,8 +91,7 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
);
|
),
|
||||||
},
|
|
||||||
),
|
),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: MediaQuery.paddingOf(context).bottom,
|
bottom: MediaQuery.paddingOf(context).bottom,
|
||||||
@@ -128,7 +103,7 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
builder: (context) =>
|
builder: (context) =>
|
||||||
CreateMatchView(onWinnerChanged: refreshGameList),
|
CreateMatchView(onWinnerChanged: loadGames),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
@@ -139,9 +114,19 @@ class _MatchViewState extends State<MatchView> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void refreshGameList() {
|
void loadGames() {
|
||||||
|
Future.wait([
|
||||||
|
db.matchDao.getAllMatches(),
|
||||||
|
Future.delayed(minimumSkeletonDuration),
|
||||||
|
]).then((results) {
|
||||||
|
if (mounted) {
|
||||||
setState(() {
|
setState(() {
|
||||||
_gameListFuture = db.matchDao.getAllMatches();
|
final loadedMatches = results[0] as List<Match>;
|
||||||
|
matches = loadedMatches
|
||||||
|
..sort((a, b) => b.createdAt.compareTo(a.createdAt));
|
||||||
|
isLoading = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:game_tracker/core/constants.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/match.dart';
|
import 'package:game_tracker/data/dto/match.dart';
|
||||||
import 'package:game_tracker/data/dto/player.dart';
|
import 'package:game_tracker/data/dto/player.dart';
|
||||||
@@ -14,8 +15,6 @@ class StatisticsView extends StatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _StatisticsViewState extends State<StatisticsView> {
|
class _StatisticsViewState extends State<StatisticsView> {
|
||||||
late Future<List<Match>> _matchesFuture;
|
|
||||||
late Future<List<Player>> _playersFuture;
|
|
||||||
List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1));
|
List<(String, int)> winCounts = List.filled(6, ('Skeleton Player', 1));
|
||||||
List<(String, int)> matchCounts = List.filled(6, ('Skeleton Player', 1));
|
List<(String, int)> matchCounts = List.filled(6, ('Skeleton Player', 1));
|
||||||
List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1));
|
List<(String, double)> winRates = List.filled(6, ('Skeleton Player', 1));
|
||||||
@@ -25,11 +24,12 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
final db = Provider.of<AppDatabase>(context, listen: false);
|
final db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
_matchesFuture = db.matchDao.getAllMatches();
|
|
||||||
_playersFuture = db.playerDao.getAllPlayers();
|
|
||||||
|
|
||||||
Future.wait([_matchesFuture, _playersFuture]).then((results) async {
|
Future.wait([
|
||||||
await Future.delayed(const Duration(milliseconds: 250));
|
db.matchDao.getAllMatches(),
|
||||||
|
db.playerDao.getAllPlayers(),
|
||||||
|
Future.delayed(minimumSkeletonDuration),
|
||||||
|
]).then((results) async {
|
||||||
final matches = results[0] as List<Match>;
|
final matches = results[0] as List<Match>;
|
||||||
final players = results[1] as List<Player>;
|
final players = results[1] as List<Player>;
|
||||||
winCounts = _calculateWinsForAllPlayers(matches, players);
|
winCounts = _calculateWinsForAllPlayers(matches, players);
|
||||||
@@ -60,7 +60,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
SizedBox(height: constraints.maxHeight * 0.01),
|
SizedBox(height: constraints.maxHeight * 0.01),
|
||||||
StatisticsTile(
|
StatisticsTile(
|
||||||
icon: Icons.sports_score,
|
icon: Icons.sports_score,
|
||||||
title: 'Wins per Player',
|
title: 'Wins',
|
||||||
width: constraints.maxWidth * 0.95,
|
width: constraints.maxWidth * 0.95,
|
||||||
values: winCounts,
|
values: winCounts,
|
||||||
itemCount: 3,
|
itemCount: 3,
|
||||||
@@ -69,7 +69,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
SizedBox(height: constraints.maxHeight * 0.02),
|
SizedBox(height: constraints.maxHeight * 0.02),
|
||||||
StatisticsTile(
|
StatisticsTile(
|
||||||
icon: Icons.percent,
|
icon: Icons.percent,
|
||||||
title: 'Winrate per Player',
|
title: 'Winrate',
|
||||||
width: constraints.maxWidth * 0.95,
|
width: constraints.maxWidth * 0.95,
|
||||||
values: winRates,
|
values: winRates,
|
||||||
itemCount: 5,
|
itemCount: 5,
|
||||||
@@ -78,7 +78,7 @@ class _StatisticsViewState extends State<StatisticsView> {
|
|||||||
SizedBox(height: constraints.maxHeight * 0.02),
|
SizedBox(height: constraints.maxHeight * 0.02),
|
||||||
StatisticsTile(
|
StatisticsTile(
|
||||||
icon: Icons.casino,
|
icon: Icons.casino,
|
||||||
title: 'Match per Player',
|
title: 'Amount of Matches',
|
||||||
width: constraints.maxWidth * 0.95,
|
width: constraints.maxWidth * 0.95,
|
||||||
values: matchCounts,
|
values: matchCounts,
|
||||||
itemCount: 10,
|
itemCount: 10,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:game_tracker/core/constants.dart';
|
||||||
import 'package:game_tracker/core/custom_theme.dart';
|
import 'package:game_tracker/core/custom_theme.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
import 'package:game_tracker/data/dto/player.dart';
|
import 'package:game_tracker/data/dto/player.dart';
|
||||||
@@ -29,6 +30,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
List<Player> selectedPlayers = [];
|
List<Player> selectedPlayers = [];
|
||||||
List<Player> suggestedPlayers = [];
|
List<Player> suggestedPlayers = [];
|
||||||
List<Player> allPlayers = [];
|
List<Player> allPlayers = [];
|
||||||
|
bool isLoading = true;
|
||||||
late final TextEditingController _searchBarController =
|
late final TextEditingController _searchBarController =
|
||||||
TextEditingController();
|
TextEditingController();
|
||||||
late final AppDatabase db;
|
late final AppDatabase db;
|
||||||
@@ -42,15 +44,16 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
db = Provider.of<AppDatabase>(context, listen: false);
|
db = Provider.of<AppDatabase>(context, listen: false);
|
||||||
|
suggestedPlayers = skeletonData;
|
||||||
loadPlayerList();
|
loadPlayerList();
|
||||||
}
|
}
|
||||||
|
|
||||||
void loadPlayerList() {
|
void loadPlayerList() {
|
||||||
_allPlayersFuture = Future.delayed(
|
_allPlayersFuture = Future.wait([
|
||||||
const Duration(milliseconds: 250),
|
db.playerDao.getAllPlayers(),
|
||||||
() => db.playerDao.getAllPlayers(),
|
Future.delayed(minimumSkeletonDuration),
|
||||||
);
|
]).then((results) => results[0] as List<Player>);
|
||||||
suggestedPlayers = skeletonData;
|
if (mounted) {
|
||||||
_allPlayersFuture.then((loadedPlayers) {
|
_allPlayersFuture.then((loadedPlayers) {
|
||||||
setState(() {
|
setState(() {
|
||||||
// If a list of available players is provided, use that list.
|
// If a list of available players is provided, use that list.
|
||||||
@@ -75,9 +78,11 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
allPlayers = [...loadedPlayers];
|
allPlayers = [...loadedPlayers];
|
||||||
suggestedPlayers = [...loadedPlayers];
|
suggestedPlayers = [...loadedPlayers];
|
||||||
}
|
}
|
||||||
|
isLoading = false;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
@@ -176,42 +181,24 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
FutureBuilder(
|
/*
|
||||||
future: _allPlayersFuture,
|
|
||||||
builder:
|
*/
|
||||||
(BuildContext context, AsyncSnapshot<List<Player>> snapshot) {
|
Expanded(
|
||||||
if (snapshot.hasError) {
|
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.report,
|
|
||||||
title: 'Error',
|
|
||||||
message: 'Player data couldn\'t\nbe loaded.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
bool doneLoading =
|
|
||||||
snapshot.connectionState == ConnectionState.done;
|
|
||||||
bool snapshotDataEmpty =
|
|
||||||
!snapshot.hasData || snapshot.data!.isEmpty;
|
|
||||||
if (doneLoading &&
|
|
||||||
(snapshotDataEmpty && allPlayers.isEmpty)) {
|
|
||||||
return const Center(
|
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.info,
|
|
||||||
title: 'Info',
|
|
||||||
message: 'No players created yet.',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
final bool isLoading =
|
|
||||||
snapshot.connectionState == ConnectionState.waiting;
|
|
||||||
return Expanded(
|
|
||||||
child: AppSkeleton(
|
child: AppSkeleton(
|
||||||
enabled: isLoading,
|
enabled: isLoading,
|
||||||
child: Visibility(
|
child: Visibility(
|
||||||
visible:
|
visible: suggestedPlayers.isNotEmpty,
|
||||||
(suggestedPlayers.isEmpty && allPlayers.isNotEmpty),
|
replacement: TopCenteredMessage(
|
||||||
replacement: ListView.builder(
|
icon: Icons.info,
|
||||||
|
title: 'Info',
|
||||||
|
message: allPlayers.isEmpty
|
||||||
|
? 'No players created yet'
|
||||||
|
: (selectedPlayers.length == allPlayers.length)
|
||||||
|
? 'No more players to add'
|
||||||
|
: 'No players found with that name',
|
||||||
|
),
|
||||||
|
child: ListView.builder(
|
||||||
itemCount: suggestedPlayers.length,
|
itemCount: suggestedPlayers.length,
|
||||||
itemBuilder: (BuildContext context, int index) {
|
itemBuilder: (BuildContext context, int index) {
|
||||||
return TextIconListTile(
|
return TextIconListTile(
|
||||||
@@ -221,31 +208,18 @@ class _PlayerSelectionState extends State<PlayerSelection> {
|
|||||||
if (!selectedPlayers.contains(
|
if (!selectedPlayers.contains(
|
||||||
suggestedPlayers[index],
|
suggestedPlayers[index],
|
||||||
)) {
|
)) {
|
||||||
selectedPlayers.add(
|
selectedPlayers.add(suggestedPlayers[index]);
|
||||||
suggestedPlayers[index],
|
|
||||||
);
|
|
||||||
widget.onChanged([...selectedPlayers]);
|
widget.onChanged([...selectedPlayers]);
|
||||||
suggestedPlayers.remove(
|
suggestedPlayers.remove(suggestedPlayers[index]);
|
||||||
suggestedPlayers[index],
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
child: TopCenteredMessage(
|
|
||||||
icon: Icons.info,
|
|
||||||
title: 'Info',
|
|
||||||
message: (selectedPlayers.length == allPlayers.length)
|
|
||||||
? 'No more players to add.'
|
|
||||||
: 'No players found with that name.',
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ void main() {
|
|||||||
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
||||||
final fakeClock = Clock(() => fixedDate);
|
final fakeClock = Clock(() => fixedDate);
|
||||||
|
|
||||||
setUp(() {
|
setUp(() async {
|
||||||
database = AppDatabase(
|
database = AppDatabase(
|
||||||
DatabaseConnection(
|
DatabaseConnection(
|
||||||
NativeDatabase.memory(),
|
NativeDatabase.memory(),
|
||||||
@@ -68,6 +68,16 @@ void main() {
|
|||||||
group: testGroup2,
|
group: testGroup2,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
await database.playerDao.addPlayersAsList(
|
||||||
|
players: [
|
||||||
|
testPlayer1,
|
||||||
|
testPlayer2,
|
||||||
|
testPlayer3,
|
||||||
|
testPlayer4,
|
||||||
|
testPlayer5,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]);
|
||||||
});
|
});
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
await database.close();
|
await database.close();
|
||||||
@@ -253,7 +263,7 @@ void main() {
|
|||||||
expect(matchCount, 0);
|
expect(matchCount, 0);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Checking if match has winner works correclty', () async {
|
test('Checking if match has winner works correctly', () async {
|
||||||
await database.matchDao.addMatch(match: testMatch1);
|
await database.matchDao.addMatch(match: testMatch1);
|
||||||
await database.matchDao.addMatch(match: testMatchOnlyGroup);
|
await database.matchDao.addMatch(match: testMatchOnlyGroup);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart' hide isNotNull;
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
@@ -21,7 +21,7 @@ void main() {
|
|||||||
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
||||||
final fakeClock = Clock(() => fixedDate);
|
final fakeClock = Clock(() => fixedDate);
|
||||||
|
|
||||||
setUp(() {
|
setUp(() async {
|
||||||
database = AppDatabase(
|
database = AppDatabase(
|
||||||
DatabaseConnection(
|
DatabaseConnection(
|
||||||
NativeDatabase.memory(),
|
NativeDatabase.memory(),
|
||||||
@@ -53,6 +53,16 @@ void main() {
|
|||||||
group: testGroup1,
|
group: testGroup1,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
await database.playerDao.addPlayersAsList(
|
||||||
|
players: [
|
||||||
|
testPlayer1,
|
||||||
|
testPlayer2,
|
||||||
|
testPlayer3,
|
||||||
|
testPlayer4,
|
||||||
|
testPlayer5,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await database.groupDao.addGroupsAsList(groups: [testGroup1, testGroup2]);
|
||||||
});
|
});
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
await database.close();
|
await database.close();
|
||||||
@@ -179,5 +189,33 @@ void main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('Adding the same group to seperate matches works correctly', () async {
|
||||||
|
final match1 = Match(name: 'Match 1', group: testGroup1);
|
||||||
|
final match2 = Match(name: 'Match 2', group: testGroup1);
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
database.matchDao.addMatch(match: match1),
|
||||||
|
database.matchDao.addMatch(match: match2),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final group1 = await database.groupMatchDao.getGroupOfMatch(
|
||||||
|
matchId: match1.id,
|
||||||
|
);
|
||||||
|
final group2 = await database.groupMatchDao.getGroupOfMatch(
|
||||||
|
matchId: match2.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(group1, isNotNull);
|
||||||
|
expect(group2, isNotNull);
|
||||||
|
|
||||||
|
final groups = [group1!, group2!];
|
||||||
|
for (final group in groups) {
|
||||||
|
expect(group.members.length, testGroup1.members.length);
|
||||||
|
expect(group.id, testGroup1.id);
|
||||||
|
expect(group.name, testGroup1.name);
|
||||||
|
expect(group.createdAt, testGroup1.createdAt);
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import 'package:clock/clock.dart';
|
import 'package:clock/clock.dart';
|
||||||
import 'package:drift/drift.dart';
|
import 'package:drift/drift.dart' hide isNotNull;
|
||||||
import 'package:drift/native.dart';
|
import 'package:drift/native.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:game_tracker/data/db/database.dart';
|
import 'package:game_tracker/data/db/database.dart';
|
||||||
@@ -21,7 +21,7 @@ void main() {
|
|||||||
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
final fixedDate = DateTime(2025, 19, 11, 00, 11, 23);
|
||||||
final fakeClock = Clock(() => fixedDate);
|
final fakeClock = Clock(() => fixedDate);
|
||||||
|
|
||||||
setUp(() {
|
setUp(() async {
|
||||||
database = AppDatabase(
|
database = AppDatabase(
|
||||||
DatabaseConnection(
|
DatabaseConnection(
|
||||||
NativeDatabase.memory(),
|
NativeDatabase.memory(),
|
||||||
@@ -50,6 +50,17 @@ void main() {
|
|||||||
players: [testPlayer4, testPlayer5, testPlayer6],
|
players: [testPlayer4, testPlayer5, testPlayer6],
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
await database.playerDao.addPlayersAsList(
|
||||||
|
players: [
|
||||||
|
testPlayer1,
|
||||||
|
testPlayer2,
|
||||||
|
testPlayer3,
|
||||||
|
testPlayer4,
|
||||||
|
testPlayer5,
|
||||||
|
testPlayer6,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
await database.groupDao.addGroup(group: testgroup);
|
||||||
});
|
});
|
||||||
tearDown(() async {
|
tearDown(() async {
|
||||||
await database.close();
|
await database.close();
|
||||||
@@ -185,5 +196,42 @@ void main() {
|
|||||||
expect(player.createdAt, testPlayer.createdAt);
|
expect(player.createdAt, testPlayer.createdAt);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'Adding the same player to seperate matches works correctly',
|
||||||
|
() async {
|
||||||
|
final playersList = [testPlayer1, testPlayer2, testPlayer3];
|
||||||
|
final match1 = Match(name: 'Match 1', players: playersList);
|
||||||
|
final match2 = Match(name: 'Match 2', players: playersList);
|
||||||
|
|
||||||
|
await Future.wait([
|
||||||
|
database.matchDao.addMatch(match: match1),
|
||||||
|
database.matchDao.addMatch(match: match2),
|
||||||
|
]);
|
||||||
|
|
||||||
|
final players1 = await database.playerMatchDao.getPlayersOfMatch(
|
||||||
|
matchId: match1.id,
|
||||||
|
);
|
||||||
|
final players2 = await database.playerMatchDao.getPlayersOfMatch(
|
||||||
|
matchId: match2.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(players1, isNotNull);
|
||||||
|
expect(players2, isNotNull);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
players1!.map((p) => p.id).toList(),
|
||||||
|
equals(players2!.map((p) => p.id).toList()),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
players1.map((p) => p.name).toList(),
|
||||||
|
equals(players2.map((p) => p.name).toList()),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
players1.map((p) => p.createdAt).toList(),
|
||||||
|
equals(players2.map((p) => p.createdAt).toList()),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user