MVP-Refactoring #139

Merged
flixcoo merged 20 commits from refactoring/68-mvp-refactoring into development 2026-01-08 20:24:01 +00:00
11 changed files with 117 additions and 67 deletions
Showing only changes of commit 6e45e9435b - Show all commits

View File

@@ -17,7 +17,10 @@ class CustomNavigationBar extends StatefulWidget {
class _CustomNavigationBarState extends State<CustomNavigationBar>
with SingleTickerProviderStateMixin {
/// Currently selected tab index
int currentIndex = 0;
/// Key count to force rebuild of tab views
int tabKeyCount = 0;
@override
@@ -119,12 +122,14 @@ class _CustomNavigationBarState extends State<CustomNavigationBar>
);
}
/// Handles tab tap events. Updates the current [index] state.
void onTabTapped(int index) {
setState(() {
currentIndex = index;
});
}
/// Returns the title of the current tab based on [currentIndex].
String _currentTabTitle(context) {
final loc = AppLocalizations.of(context);
switch (currentIndex) {

View File

@@ -18,15 +18,17 @@ class CreateGroupView extends StatefulWidget {
}
class _CreateGroupViewState extends State<CreateGroupView> {
final _groupNameController = TextEditingController();
late final AppDatabase db;
sneeex marked this conversation as resolved
Review

warum für den rest comments aber nicht für db?

warum für den rest comments aber nicht für db?
Review

Habe Datenbank und isLoading nicht kommentiert, weil die so allgegenwärtig sind, dass die eigentlich keinen kommentar brauchen. Soll ich einen hinzufügen?

Habe Datenbank und `isLoading` nicht kommentiert, weil die so allgegenwärtig sind, dass die eigentlich keinen kommentar brauchen. Soll ich einen hinzufügen?
/// Controller for the group name input field
final _groupNameController = TextEditingController();
/// List of currently selected players
List<Player> selectedPlayers = [];
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
_groupNameController.addListener(() {
setState(() {});

View File

@@ -21,7 +21,11 @@ class GroupsView extends StatefulWidget {
class _GroupsViewState extends State<GroupsView> {
late final AppDatabase db;
flixcoo marked this conversation as resolved
Review

hier ebenfalls kein comment?

hier ebenfalls kein comment?
/// Loaded groups from the database
late List<Group> loadedGroups;
/// Loading state
bool isLoading = true;
List<Group> groups = List.filled(

View File

@@ -21,9 +21,17 @@ class HomeView extends StatefulWidget {
class _HomeViewState extends State<HomeView> {
bool isLoading = true;
flixcoo marked this conversation as resolved
Review

hier ebenfalls kein comment?

hier ebenfalls kein comment?
/// Amount of matches in the database
int matchCount = 0;
/// Amount of groups in the database
int groupCount = 0;
/// Loaded recent matches from the database
List<Match> loadedRecentMatches = [];
/// Recent matches to display, initially filled with skeleton matches
List<Match> recentMatches = List.filled(
2,
Match(
@@ -42,32 +50,7 @@ class _HomeViewState extends State<HomeView> {
@override
void initState() {
super.initState();
final db = Provider.of<AppDatabase>(context, listen: false);
Future.wait([
db.matchDao.getMatchCount(),
db.groupDao.getGroupCount(),
db.matchDao.getAllMatches(),
Future.delayed(Constants.minimumSkeletonDuration),
]).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) {
setState(() {
isLoading = false;
});
}
});
loadHomeViewData();
}
@override
@@ -230,6 +213,40 @@ class _HomeViewState extends State<HomeView> {
);
}
/// Loads the data for the HomeView from the database.
/// This includes the match count, group count, and recent matches.
void loadHomeViewData() {
final db = Provider.of<AppDatabase>(context, listen: false);
Future.wait([
db.matchDao.getMatchCount(),
db.groupDao.getGroupCount(),
db.matchDao.getAllMatches(),
Future.delayed(Constants.minimumSkeletonDuration),
]).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) {
setState(() {
isLoading = false;
});
}
});
}
/// Generates a text representation of the players in the match.
/// If the match has a group, it returns the group name and the number of additional players.
/// If there is no group, it returns the count of players.
String _getPlayerText(Match game, context) {
final loc = AppLocalizations.of(context);
if (game.group == null) {

View File

@@ -20,10 +20,12 @@ class ChooseGameView extends StatefulWidget {
}
class _ChooseGameViewState extends State<ChooseGameView> {
late int selectedGameIndex;
/// Controller for the search bar
final TextEditingController searchBarController = TextEditingController();
/// Currently selected game index
late int selectedGameIndex;
@override
void initState() {
selectedGameIndex = widget.initialGameIndex;

View File

@@ -136,8 +136,7 @@ class _ChooseGroupViewState extends State<ChooseGroupView> {
);
}
/// Filters the groups based on the search query.
/// TODO: Maybe implement also targetting player names?
/// Filters the groups based on the search [query].
void filterGroups(String query) {
setState(() {
if (query.isEmpty) {

View File

@@ -19,6 +19,7 @@ class ChooseRulesetView extends StatefulWidget {
}
class _ChooseRulesetViewState extends State<ChooseRulesetView> {
/// Currently selected ruleset index
late int selectedRulesetIndex;
@override

View File

@@ -26,7 +26,6 @@ class CreateMatchView extends StatefulWidget {
}
class _CreateMatchViewState extends State<CreateMatchView> {
/// Reference to the app database
late final AppDatabase db;
/// Controller for the match name input field

View File

@@ -18,8 +18,12 @@ class MatchResultView extends StatefulWidget {
}
class _MatchResultViewState extends State<MatchResultView> {
late final List<Player> allPlayers;
late final AppDatabase db;
/// List of all players who participated in the match
late final List<Player> allPlayers;
/// Currently selected winner player
Player? _selectedPlayer;
@override
@@ -132,6 +136,8 @@ class _MatchResultViewState extends State<MatchResultView> {
);
}
/// Handles saving or removing the winner in the database
/// based on the current selection.
Future<void> _handleWinnerSaving() async {
if (_selectedPlayer == null) {
await db.matchDao.removeWinner(matchId: widget.match.id);
@@ -144,6 +150,10 @@ class _MatchResultViewState extends State<MatchResultView> {
widget.onWinnerChanged?.call();
}
/// Retrieves all players associated with the given [match].
/// This includes players directly assigned to the match
/// as well as members of the group (if any).
/// The returned list is sorted alphabetically by player name.
List<Player> getAllPlayers(Match match) {
List<Player> players = [];

View File

@@ -28,6 +28,8 @@ class _MatchViewState extends State<MatchView> {
late final AppDatabase db;
flixcoo marked this conversation as resolved
Review

kein comment?

kein comment?
bool isLoading = true;
flixcoo marked this conversation as resolved
Review

kein comment?

kein comment?
/// Loaded matches from the database,
/// initially filled with skeleton matches
List<Match> matches = List.filled(
4,
Match(
@@ -44,7 +46,6 @@ class _MatchViewState extends State<MatchView> {
@override
void initState() {
super.initState();
db = Provider.of<AppDatabase>(context, listen: false);
loadGames();
}
@@ -117,6 +118,7 @@ class _MatchViewState extends State<MatchView> {
);
}
/// Loads the games from the database and sorts them by creation date.
void loadGames() {
Future.wait([
db.matchDao.getAllMatches(),

View File

@@ -25,28 +25,7 @@ class _StatisticsViewState extends State<StatisticsView> {
@override
void initState() {
super.initState();
final db = Provider.of<AppDatabase>(context, listen: false);
Future.wait([
db.matchDao.getAllMatches(),
db.playerDao.getAllPlayers(),
Future.delayed(Constants.minimumSkeletonDuration),
]).then((results) async {
if (!mounted) return;
final matches = results[0] as List<Match>;
final players = results[1] as List<Player>;
winCounts = _calculateWinsForAllPlayers(matches, players, context);
matchCounts = _calculateMatchAmountsForAllPlayers(
matches,
players,
context,
);
winRates = computeWinRatePercent(wins: winCounts, matches: matchCounts);
setState(() {
isLoading = false;
});
});
loadStatisticData();
}
@override
@@ -118,13 +97,43 @@ class _StatisticsViewState extends State<StatisticsView> {
);
}
/// Loads matches and players from the database
/// and calculates statistics for each player
void loadStatisticData() {
final db = Provider.of<AppDatabase>(context, listen: false);
Future.wait([
db.matchDao.getAllMatches(),
db.playerDao.getAllPlayers(),
Future.delayed(Constants.minimumSkeletonDuration),
]).then((results) async {
if (!mounted) return;
final matches = results[0] as List<Match>;
final players = results[1] as List<Player>;
winCounts = _calculateWinsForAllPlayers(
matches: matches,
players: players,
context: context,
);
matchCounts = _calculateMatchAmountsForAllPlayers(
matches: matches,
players: players,
context: context,
);
winRates = computeWinRatePercent(wins: winCounts, matches: matchCounts);
setState(() {
isLoading = false;
});
});
}
/// Calculates the number of wins for each player
/// and returns a sorted list of tuples (playerName, winCount)
List<(String, int)> _calculateWinsForAllPlayers(
List<Match> matches,
List<Player> players,
BuildContext context,
) {
List<(String, int)> _calculateWinsForAllPlayers({
required List<Match> matches,
required List<Player> players,
required BuildContext context,
}) {
List<(String, int)> winCounts = [];
final loc = AppLocalizations.of(context);
@@ -169,11 +178,11 @@ class _StatisticsViewState extends State<StatisticsView> {
/// Calculates the number of matches played for each player
/// and returns a sorted list of tuples (playerName, matchCount)
List<(String, int)> _calculateMatchAmountsForAllPlayers(
List<Match> matches,
List<Player> players,
BuildContext context,
) {
List<(String, int)> _calculateMatchAmountsForAllPlayers({
required List<Match> matches,
required List<Player> players,
required BuildContext context,
}) {
List<(String, int)> matchCounts = [];
final loc = AppLocalizations.of(context);