Bearbeiten und Löschen von Matches #171
@@ -21,11 +21,19 @@ import 'package:tallee/presentation/widgets/tiles/choose_tile.dart';
|
|||||||
class CreateMatchView extends StatefulWidget {
|
class CreateMatchView extends StatefulWidget {
|
||||||
/// A view that allows creating a new match
|
/// A view that allows creating a new match
|
||||||
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
|
/// [onWinnerChanged]: Optional callback invoked when the winner is changed
|
||||||
const CreateMatchView({super.key, this.onWinnerChanged, this.match});
|
const CreateMatchView({
|
||||||
|
super.key,
|
||||||
|
this.onWinnerChanged,
|
||||||
|
this.match,
|
||||||
|
this.onMatchUpdated,
|
||||||
|
});
|
||||||
|
|
||||||
/// Optional callback invoked when the winner is changed
|
/// Optional callback invoked when the winner is changed
|
||||||
final VoidCallback? onWinnerChanged;
|
final VoidCallback? onWinnerChanged;
|
||||||
|
|
||||||
|
/// Optional callback invoked when the match is updated
|
||||||
|
final void Function(Match)? onMatchUpdated;
|
||||||
|
|
||||||
/// An optional match to prefill the fields
|
/// An optional match to prefill the fields
|
||||||
final Match? match;
|
final Match? match;
|
||||||
|
|
||||||
@@ -52,15 +60,11 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
/// If a group is selected, this list contains all players from [playerList]
|
/// If a group is selected, this list contains all players from [playerList]
|
||||||
/// who are not members of the selected group. If no group is selected,
|
/// who are not members of the selected group. If no group is selected,
|
||||||
/// this list is identical to [playerList].
|
/// this list is identical to [playerList].
|
||||||
List<Player> filteredPlayerList = [];
|
/*List<Player> filteredPlayerList = [];*/
|
||||||
|
|
||||||
/// The currently selected group
|
/// The currently selected group
|
||||||
Group? selectedGroup;
|
Group? selectedGroup;
|
||||||
|
|
||||||
/// The index of the currently selected group in [groupsList] to mark it in
|
|
||||||
/// the [ChooseGroupView]
|
|
||||||
String selectedGroupId = '';
|
|
||||||
|
|
||||||
/// The index of the currently selected game in [games] to mark it in
|
/// The index of the currently selected game in [games] to mark it in
|
||||||
/// the [ChooseGameView]
|
/// the [ChooseGameView]
|
||||||
int selectedGameIndex = -1;
|
int selectedGameIndex = -1;
|
||||||
@@ -86,24 +90,12 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
]).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>;
|
||||||
setState(() {
|
|
||||||
filteredPlayerList = List.from(playerList);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// If a match is provided, prefill the fields
|
// If a match is provided, prefill the fields
|
||||||
if (widget.match != null) {
|
if (widget.match != null) {
|
||||||
final match = widget.match!;
|
prefillMatchDetails();
|
||||||
_matchNameController.text = match.name;
|
|
||||||
selectedGroup = match.group;
|
|
||||||
selectedGroupId = match.group?.id ?? '';
|
|
||||||
selectedPlayers = match.players ?? [];
|
|
||||||
if (selectedGroup != null) {
|
|
||||||
filteredPlayerList = playerList
|
|
||||||
.where((p) => !selectedGroup!.members.any((m) => m.id == p.id))
|
|
||||||
.toList();
|
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -130,13 +122,16 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
final buttonText = widget.match != null
|
final buttonText = widget.match != null
|
||||||
? loc.save_changes
|
? loc.save_changes
|
||||||
: loc.create_match;
|
: loc.create_match;
|
||||||
|
final viewTitle = widget.match != null
|
||||||
|
? loc.edit_match
|
||||||
|
: loc.create_new_match;
|
||||||
|
|
||||||
return ScaffoldMessenger(
|
return ScaffoldMessenger(
|
||||||
key: _scaffoldMessengerKey,
|
key: _scaffoldMessengerKey,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
resizeToAvoidBottomInset: false,
|
resizeToAvoidBottomInset: false,
|
||||||
backgroundColor: CustomTheme.backgroundColor,
|
backgroundColor: CustomTheme.backgroundColor,
|
||||||
appBar: AppBar(title: Text(loc.create_new_match)),
|
appBar: AppBar(title: Text(viewTitle)),
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
@@ -178,36 +173,43 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
? loc.none_group
|
? loc.none_group
|
||||||
: selectedGroup!.name,
|
: selectedGroup!.name,
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
|
// Remove all players from the previously selected group from
|
||||||
|
// the selected players list, in case the user deselects the
|
||||||
|
// group or selects a different group.
|
||||||
|
selectedPlayers.removeWhere(
|
||||||
|
(player) =>
|
||||||
|
selectedGroup?.members.any(
|
||||||
|
(member) => member.id == player.id,
|
||||||
|
) ??
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
|
||||||
selectedGroup = await Navigator.of(context).push(
|
selectedGroup = await Navigator.of(context).push(
|
||||||
adaptivePageRoute(
|
adaptivePageRoute(
|
||||||
builder: (context) => ChooseGroupView(
|
builder: (context) => ChooseGroupView(
|
||||||
groups: groupsList,
|
groups: groupsList,
|
||||||
initialGroupId: selectedGroupId,
|
initialGroupId: selectedGroup?.id ?? '',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
selectedGroupId = selectedGroup?.id ?? '';
|
|
||||||
if (selectedGroup != null) {
|
setState(() {
|
||||||
filteredPlayerList = playerList
|
if (selectedGroup != null) {
|
||||||
.where(
|
setState(() {
|
||||||
(p) =>
|
selectedPlayers = [...selectedGroup!.members];
|
||||||
!selectedGroup!.members.any((m) => m.id == p.id),
|
});
|
||||||
)
|
}
|
||||||
.toList();
|
});
|
||||||
} else {
|
|
||||||
filteredPlayerList = List.from(playerList);
|
|
||||||
}
|
|
||||||
setState(() {});
|
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: PlayerSelection(
|
child: PlayerSelection(
|
||||||
key: ValueKey(selectedGroup?.id ?? 'no_group'),
|
key: ValueKey(selectedGroup?.id ?? 'no_group'),
|
||||||
initialSelectedPlayers: selectedPlayers,
|
initialSelectedPlayers: selectedPlayers,
|
||||||
availablePlayers: filteredPlayerList,
|
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() {
|
setState(() {
|
||||||
selectedPlayers = value;
|
selectedPlayers = value;
|
||||||
|
removeGroupWhenNoMemberLeft();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -235,51 +237,22 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
/// - A ruleset is selected AND
|
/// - A ruleset is selected AND
|
||||||
/// - Either a group is selected OR at least 2 players are selected
|
/// - Either a group is selected OR at least 2 players are selected
|
||||||
bool _enableCreateGameButton() {
|
bool _enableCreateGameButton() {
|
||||||
return (selectedGroup != null ||
|
return (selectedGroup != null || (selectedPlayers.length > 1));
|
||||||
|
flixcoo marked this conversation as resolved
|
|||||||
(selectedPlayers.length > 1));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If a match was provied to the view, it updates the match in the database
|
||||||
|
// and navigates back to the previous screen.
|
||||||
|
// If no match was provided, it creates a new match in the database and
|
||||||
|
// navigates to the MatchResultView for the newly created match.
|
||||||
void buttonNavigation(BuildContext context) async {
|
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) {
|
if (widget.match != null) {
|
||||||
// TODO: Implement updating match logic here
|
await updateMatch();
|
||||||
Navigator.pop(context);
|
if (context.mounted) {
|
||||||
|
Navigator.pop(context);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Match match = Match(
|
final match = await createMatch();
|
||||||
name: _matchNameController.text.isEmpty
|
|
||||||
? (hintText ?? '')
|
|
||||||
: _matchNameController.text.trim(),
|
|
||||||
createdAt: DateTime.now(),
|
|
||||||
group: selectedGroup,
|
|
||||||
players: selectedPlayers,
|
|
||||||
game: gameToUse
|
|
||||||
);
|
|
||||||
await db.matchDao.addMatch(match: match);
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.pushReplacement(
|
Navigator.pushReplacement(
|
||||||
context,
|
context,
|
||||||
@@ -294,4 +267,130 @@ class _CreateMatchViewState extends State<CreateMatchView> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Updates attributes of the existing match in the database based on the
|
||||||
|
/// changes made in the edit view.
|
||||||
|
Future<void> updateMatch() async {
|
||||||
|
//TODO: Remove when Games implemented
|
||||||
|
final tempGame = await getTemporaryGame();
|
||||||
|
|
||||||
|
final updatedMatch = Match(
|
||||||
|
id: widget.match!.id,
|
||||||
|
name: _matchNameController.text.isEmpty
|
||||||
|
? (hintText ?? '')
|
||||||
|
: _matchNameController.text.trim(),
|
||||||
|
group: selectedGroup,
|
||||||
|
players: selectedPlayers,
|
||||||
|
game: tempGame,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (widget.match!.name != updatedMatch.name) {
|
||||||
|
await db.matchDao.updateMatchName(
|
||||||
|
matchId: widget.match!.id,
|
||||||
|
newName: updatedMatch.name,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (widget.match!.group?.id != updatedMatch.group?.id) {
|
||||||
|
await db.matchDao.updateMatchGroup(
|
||||||
|
matchId: widget.match!.id,
|
||||||
|
newGroupId: updatedMatch.group?.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add players who are in updatedMatch but not in the original match
|
||||||
|
for (var player in updatedMatch.players) {
|
||||||
|
if (!widget.match!.players.any((p) => p.id == player.id)) {
|
||||||
|
await db.playerMatchDao.addPlayerToMatch(
|
||||||
|
matchId: widget.match!.id,
|
||||||
|
playerId: player.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove players who are in the original match but not in updatedMatch
|
||||||
|
for (var player in widget.match!.players) {
|
||||||
|
if (!updatedMatch.players.any((p) => p.id == player.id)) {
|
||||||
|
await db.playerMatchDao.removePlayerFromMatch(
|
||||||
|
matchId: widget.match!.id,
|
||||||
|
playerId: player.id,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
widget.onMatchUpdated?.call(updatedMatch);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Match> createMatch() async {
|
||||||
|
final tempGame = await getTemporaryGame();
|
||||||
|
|
||||||
|
Match match = Match(
|
||||||
|
name: _matchNameController.text.isEmpty
|
||||||
|
? (hintText ?? '')
|
||||||
|
: _matchNameController.text.trim(),
|
||||||
|
createdAt: DateTime.now(),
|
||||||
|
group: selectedGroup,
|
||||||
|
players: selectedPlayers,
|
||||||
|
game: tempGame,
|
||||||
|
);
|
||||||
|
await db.matchDao.addMatch(match: match);
|
||||||
|
return match;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Remove when games fully implemented
|
||||||
|
Future<Game> getTemporaryGame() async {
|
||||||
|
Game? game;
|
||||||
|
|
||||||
|
// No game is selected
|
||||||
|
if (selectedGameIndex == -1) {
|
||||||
|
// Use the first game as default if none selected
|
||||||
|
final selectedGame = games[0];
|
||||||
|
game = 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];
|
||||||
|
game = 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: game);
|
||||||
|
return game;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If a match was provided to the view, this method prefills the input fields
|
||||||
|
void prefillMatchDetails() {
|
||||||
|
final match = widget.match!;
|
||||||
|
_matchNameController.text = match.name;
|
||||||
|
selectedPlayers = match.players;
|
||||||
|
|
||||||
|
if (match.group != null) {
|
||||||
|
selectedGroup = match.group;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If none of the selected players are from the currently selected group,
|
||||||
|
// the group is also deselected.
|
||||||
|
Future<void> removeGroupWhenNoMemberLeft() async {
|
||||||
|
if (selectedGroup == null) return;
|
||||||
|
|
||||||
|
if (!selectedPlayers.any(
|
||||||
|
(player) =>
|
||||||
|
selectedGroup!.members.any((member) => member.id == player.id),
|
||||||
|
)) {
|
||||||
|
setState(() {
|
||||||
|
selectedGroup = null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user
sollte nicht auch hier als bedingung rein, dass game ausgewählt ist (auch wenns gerade noch keine funktion hat)
Geändert