Merge branch 'development' into feature/120-bearbeiten-und-loeschen-von-matches

# Conflicts:
#	lib/presentation/views/main_menu/match_view/create_match/create_match_view.dart
This commit is contained in:
2026-03-05 11:00:43 +01:00
62 changed files with 8577 additions and 2259 deletions

View File

@@ -158,6 +158,7 @@ class _CreateGroupViewState extends State<CreateGroupView> {
updatedGroup = Group(
id: widget.groupToEdit!.id,
name: _groupNameController.text.trim(),
description: '',
members: selectedPlayers,
);
//TODO: Implement group editing in database

View File

@@ -35,7 +35,8 @@ class _GroupViewState extends State<GroupView> {
7,
Group(
name: 'Skeleton Group',
members: List.filled(6, Player(name: 'Skeleton Player')),
description: '',
members: List.filled(6, Player(name: 'Skeleton Player', description: '')),
),
);

View File

@@ -2,7 +2,9 @@ import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.dart';
import 'package:tallee/core/enums.dart';
import 'package:tallee/data/db/database.dart';
import 'package:tallee/data/dto/game.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
@@ -40,13 +42,16 @@ class _HomeViewState extends State<HomeView> {
2,
Match(
name: 'Skeleton Match',
game: Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''),
group: Group(
name: 'Skeleton Group',
description: '',
members: [
Player(name: 'Skeleton Player 1'),
Player(name: 'Skeleton Player 2'),
Player(name: 'Skeleton Player 1', description: ''),
Player(name: 'Skeleton Player 2', description: ''),
],
),
notes: '',
),
);
@@ -99,9 +104,7 @@ class _HomeViewState extends State<HomeView> {
if (recentMatches.isNotEmpty)
for (Match match in recentMatches)
Padding(
padding: const EdgeInsets.symmetric(
vertical: 6.0,
),
padding: const EdgeInsets.symmetric(vertical: 6.0),
child: MatchTile(
compact: true,
width: constraints.maxWidth * 0.9,
@@ -110,19 +113,15 @@ class _HomeViewState extends State<HomeView> {
await Navigator.of(context).push(
adaptivePageRoute(
fullscreenDialog: true,
builder: (context) =>
MatchResultView(match: match),
builder: (context) => MatchResultView(match: match),
),
);
await updatedWinnerinRecentMatches(match.id);
await updatedWinnerInRecentMatches(match.id);
},
),
)
else
Center(
heightFactor: 5,
child: Text(loc.no_recent_matches_available),
),
Center(heightFactor: 5, child: Text(loc.no_recent_matches_available)),
],
),
),
@@ -138,40 +137,22 @@ class _HomeViewState extends State<HomeView> {
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
QuickCreateButton(
text: 'Category 1',
onPressed: () {},
),
QuickCreateButton(
text: 'Category 2',
onPressed: () {},
),
QuickCreateButton(text: 'Category 1', onPressed: () {}),
QuickCreateButton(text: 'Category 2', onPressed: () {}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
QuickCreateButton(
text: 'Category 3',
onPressed: () {},
),
QuickCreateButton(
text: 'Category 4',
onPressed: () {},
),
QuickCreateButton(text: 'Category 3', onPressed: () {}),
QuickCreateButton(text: 'Category 4', onPressed: () {}),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
QuickCreateButton(
text: 'Category 5',
onPressed: () {},
),
QuickCreateButton(
text: 'Category 6',
onPressed: () {},
),
QuickCreateButton(text: 'Category 5', onPressed: () {}),
QuickCreateButton(text: 'Category 6', onPressed: () {}),
],
),
],
@@ -200,11 +181,9 @@ class _HomeViewState extends State<HomeView> {
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();
recentMatches = (loadedRecentMatches..sort((a, b) => b.createdAt.compareTo(a.createdAt)))
.take(2)
.toList();
if (mounted) {
setState(() {
isLoading = false;
@@ -214,7 +193,7 @@ class _HomeViewState extends State<HomeView> {
}
/// Updates the winner information for a specific match in the recent matches list.
Future<void> updatedWinnerinRecentMatches(String matchId) async {
Future<void> updatedWinnerInRecentMatches(String matchId) async {
final db = Provider.of<AppDatabase>(context, listen: false);
final winner = await db.matchDao.getWinner(matchId: matchId);
final matchIndex = recentMatches.indexWhere((match) => match.id == matchId);

View File

@@ -5,6 +5,7 @@ import 'package:tallee/core/constants.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/dto/game.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
@@ -65,7 +66,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
int selectedGameIndex = -1;
/// The currently selected players
List<Player>? selectedPlayers;
List<Player> selectedPlayers = [];
/// GlobalKey for ScaffoldMessenger to show snackbars
final _scaffoldMessengerKey = GlobalKey<ScaffoldMessengerState>();
@@ -119,7 +120,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
}
List<(String, String, Ruleset)> games = [
('Example Game 1', 'This is a description', Ruleset.leastPoints),
('Example Game 1', 'This is a description', Ruleset.lowestScore),
('Example Game 2', '', Ruleset.singleWinner),
];
@@ -190,8 +191,8 @@ class _CreateMatchViewState extends State<CreateMatchView> {
filteredPlayerList = playerList
.where(
(p) =>
!selectedGroup!.members.any((m) => m.id == p.id),
)
!selectedGroup!.members.any((m) => m.id == p.id),
)
.toList();
} else {
filteredPlayerList = List.from(playerList);
@@ -202,7 +203,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
Expanded(
child: PlayerSelection(
key: ValueKey(selectedGroup?.id ?? 'no_group'),
initialSelectedPlayers: selectedPlayers ?? [],
initialSelectedPlayers: selectedPlayers,
availablePlayers: filteredPlayerList,
onChanged: (value) {
setState(() {
@@ -235,10 +236,36 @@ class _CreateMatchViewState extends State<CreateMatchView> {
/// - Either a group is selected OR at least 2 players are selected
bool _enableCreateGameButton() {
return (selectedGroup != null ||
(selectedPlayers != null && selectedPlayers!.length > 1));
(selectedPlayers.length > 1));
}
void buttonNavigation(BuildContext context) async {
// Use a game from the games list
Game? gameToUse;
if (selectedGameIndex == -1) {
// Use the first game as default if none selected
final selectedGame = games[0];
gameToUse = Game(
name: selectedGame.$1,
description: selectedGame.$2,
ruleset: selectedGame.$3,
color: GameColor.blue,
icon: '',
);
} else {
// Use the selected game from the list
final selectedGame = games[selectedGameIndex];
gameToUse = Game(
name: selectedGame.$1,
description: selectedGame.$2,
ruleset: selectedGame.$3,
color: GameColor.blue,
icon: '',
);
}
// Add the game to the database if it doesn't exist
await db.gameDao.addGame(game: gameToUse);
if (widget.match != null) {
// TODO: Implement updating match logic here
Navigator.pop(context);
@@ -250,6 +277,7 @@ class _CreateMatchViewState extends State<CreateMatchView> {
createdAt: DateTime.now(),
group: selectedGroup,
players: selectedPlayers,
game: gameToUse
);
await db.matchDao.addMatch(match: match);
if (context.mounted) {
@@ -266,4 +294,4 @@ class _CreateMatchViewState extends State<CreateMatchView> {
}
}
}
}
}

View File

@@ -153,12 +153,10 @@ class _MatchResultViewState extends State<MatchResultView> {
List<Player> getAllPlayers(Match match) {
List<Player> players = [];
if (match.group == null && match.players != null) {
players = [...match.players!];
} else if (match.group != null && match.players != null) {
players = [...match.players!, ...match.group!.members];
if (match.group == null) {
players = [...match.players];
} else {
players = [...match.group!.members];
players = [...match.players, ...match.group!.members];
}
players.sort((a, b) => a.name.compareTo(b.name));

View File

@@ -6,7 +6,9 @@ import 'package:provider/provider.dart';
import 'package:tallee/core/adaptive_page_route.dart';
import 'package:tallee/core/constants.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/dto/game.dart';
import 'package:tallee/data/dto/group.dart';
import 'package:tallee/data/dto/match.dart';
import 'package:tallee/data/dto/player.dart';
@@ -36,12 +38,15 @@ class _MatchViewState extends State<MatchView> {
4,
Match(
name: 'Skeleton match name',
game: Game(name: '', ruleset: Ruleset.singleWinner, description: '', color: GameColor.blue, icon: ''),
group: Group(
name: 'Group name',
members: List.filled(5, Player(name: 'Player')),
description: '',
members: List.filled(5, Player(name: 'Player', description: '')),
),
winner: Player(name: 'Player'),
players: [Player(name: 'Player')],
winner: Player(name: 'Player', description: ''),
players: [Player(name: 'Player', description: '')],
notes: '',
),
);

View File

@@ -167,7 +167,7 @@ class _StatisticsViewState extends State<StatisticsView> {
final playerId = winCounts[i].$1;
final player = players.firstWhere(
(p) => p.id == playerId,
orElse: () => Player(id: playerId, name: loc.not_available),
orElse: () => Player(id: playerId, name: loc.not_available, description: ''),
);
winCounts[i] = (player.name, winCounts[i].$2);
}
@@ -202,19 +202,17 @@ class _StatisticsViewState extends State<StatisticsView> {
}
}
}
if (match.players != null) {
final members = match.players!.map((p) => p.id).toList();
for (var playerId in members) {
final index = matchCounts.indexWhere((entry) => entry.$1 == playerId);
// -1 means player not found in matchCounts
if (index != -1) {
final current = matchCounts[index].$2;
final members = match.players.map((p) => p.id).toList();
for (var playerId in members) {
final index = matchCounts.indexWhere((entry) => entry.$1 == playerId);
// -1 means player not found in matchCounts
if (index != -1) {
final current = matchCounts[index].$2;
matchCounts[index] = (playerId, current + 1);
} else {
matchCounts.add((playerId, 1));
}
}
}
}
// Adding all players with zero matches
@@ -231,7 +229,7 @@ class _StatisticsViewState extends State<StatisticsView> {
final playerId = matchCounts[i].$1;
final player = players.firstWhere(
(p) => p.id == playerId,
orElse: () => Player(id: playerId, name: loc.not_available),
orElse: () => Player(id: playerId, name: loc.not_available, description: ''),
);
matchCounts[i] = (player.name, matchCounts[i].$2);
}

View File

@@ -62,7 +62,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
/// Skeleton data used while loading players.
late final List<Player> skeletonData = List.filled(
7,
Player(name: 'Player 0'),
Player(name: 'Player 0', description: ''),
);
@override
@@ -276,7 +276,7 @@ class _PlayerSelectionState extends State<PlayerSelection> {
final loc = AppLocalizations.of(context);
final playerName = _searchBarController.text.trim();
final createdPlayer = Player(name: playerName);
final createdPlayer = Player(name: playerName, description: '');
final success = await db.playerDao.addPlayer(player: createdPlayer);
if (!context.mounted) return;

View File

@@ -91,7 +91,7 @@ class _MatchTileState extends State<MatchTile> {
const SizedBox(width: 6),
Expanded(
child: Text(
'${group.name}${widget.match.players != null ? ' + ${widget.match.players?.length}' : ''}',
'${group.name} + ${widget.match.players.length}',
style: const TextStyle(fontSize: 14, color: Colors.grey),
overflow: TextOverflow.ellipsis,
),
@@ -106,7 +106,7 @@ class _MatchTileState extends State<MatchTile> {
const SizedBox(width: 6),
Expanded(
child: Text(
'${widget.match.players!.length} ${loc.players}',
'${widget.match.players.length} ${loc.players}',
style: const TextStyle(fontSize: 14, color: Colors.grey),
overflow: TextOverflow.ellipsis,
),
@@ -241,12 +241,10 @@ class _MatchTileState extends State<MatchTile> {
final playerIds = <String>{};
// Add players from game.players
if (widget.match.players != null) {
for (var player in widget.match.players!) {
if (!playerIds.contains(player.id)) {
allPlayers.add(player);
playerIds.add(player.id);
}
for (var player in widget.match.players) {
if (!playerIds.contains(player.id)) {
allPlayers.add(player);
playerIds.add(player.id);
}
}