diff --git a/lib/data/dto/match.dart b/lib/data/dto/match.dart index fcb4dae..9570f66 100644 --- a/lib/data/dto/match.dart +++ b/lib/data/dto/match.dart @@ -9,7 +9,7 @@ class Match { final String name; final List? players; final Group? group; - final Player? winner; + Player? winner; Match({ String? id, diff --git a/lib/presentation/views/main_menu/home_view.dart b/lib/presentation/views/main_menu/home_view.dart index 170adb4..affbe92 100644 --- a/lib/presentation/views/main_menu/home_view.dart +++ b/lib/presentation/views/main_menu/home_view.dart @@ -1,14 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/constants.dart'; import 'package:game_tracker/data/db/database.dart'; import 'package:game_tracker/data/dto/group.dart'; import 'package:game_tracker/data/dto/match.dart'; import 'package:game_tracker/data/dto/player.dart'; import 'package:game_tracker/l10n/generated/app_localizations.dart'; +import 'package:game_tracker/presentation/views/main_menu/match_view/match_result_view.dart'; import 'package:game_tracker/presentation/widgets/app_skeleton.dart'; import 'package:game_tracker/presentation/widgets/buttons/quick_create_button.dart'; import 'package:game_tracker/presentation/widgets/tiles/info_tile.dart'; -import 'package:game_tracker/presentation/widgets/tiles/match_summary_tile.dart'; +import 'package:game_tracker/presentation/widgets/tiles/match_tile.dart'; import 'package:game_tracker/presentation/widgets/tiles/quick_info_tile.dart'; import 'package:provider/provider.dart'; @@ -43,7 +45,6 @@ class _HomeViewState extends State { Player(name: 'Skeleton Player 2'), ], ), - winner: Player(name: 'Skeleton Player 1'), ), ); @@ -90,121 +91,92 @@ class _HomeViewState extends State { child: InfoTile( width: constraints.maxWidth * 0.95, title: loc.recent_matches, - icon: Icons.timer, - content: Padding( - padding: const EdgeInsets.symmetric(horizontal: 40.0), - child: Visibility( - visible: !isLoading && loadedRecentMatches.isNotEmpty, - replacement: Center( - heightFactor: 12, - child: Text( - AppLocalizations.of( - context, - ).no_recent_matches_available, + icon: Icons.history_rounded, + content: Column( + children: [ + if (recentMatches.isNotEmpty) + for (Match match in recentMatches) + Padding( + padding: const EdgeInsets.symmetric( + vertical: 6.0, + ), + child: MatchTile( + compact: true, + width: constraints.maxWidth * 0.9, + match: match, + onTap: () async { + await Navigator.of(context).push( + adaptivePageRoute( + fullscreenDialog: true, + builder: (context) => + MatchResultView(match: match), + ), + ); + await updatedWinnerinRecentMatches(match.id); + }, + ), + ) + else + Center( + heightFactor: 5, + child: Text(loc.no_recent_matches_available), ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - MatchSummaryTile( - matchTitle: recentMatches[0].name, - game: 'Winner', - ruleset: 'Ruleset', - players: _getPlayerText( - recentMatches[0], - context, - ), - winner: recentMatches[0].winner == null - ? AppLocalizations.of( - context, - ).match_in_progress - : recentMatches[0].winner!.name, - ), - const Padding( - padding: EdgeInsets.symmetric(vertical: 8.0), - child: Divider(), - ), - if (loadedRecentMatches.length > 1) ...[ - MatchSummaryTile( - matchTitle: recentMatches[1].name, - game: 'Winner', - ruleset: 'Ruleset', - players: _getPlayerText( - recentMatches[1], - context, - ), - winner: recentMatches[1].winner == null - ? AppLocalizations.of( - context, - ).match_in_progress - : recentMatches[1].winner!.name, - ), - const SizedBox(height: 8), - ] else ...[ - Center( - heightFactor: 5.35, - child: Text( - AppLocalizations.of( - context, - ).no_second_match_available, - ), - ), - ], - ], - ), - ), + ], ), ), ), - InfoTile( - width: constraints.maxWidth * 0.95, - title: loc.quick_create, - icon: Icons.add_box_rounded, - content: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - 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: () {}, - ), - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - QuickCreateButton( - text: 'Category 5', - onPressed: () {}, - ), - QuickCreateButton( - text: 'Category 6', - onPressed: () {}, - ), - ], - ), - ], + Padding( + padding: EdgeInsets.zero, + child: InfoTile( + width: constraints.maxWidth * 0.95, + title: loc.quick_create, + icon: Icons.add_box_rounded, + content: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + 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: () {}, + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + QuickCreateButton( + text: 'Category 5', + onPressed: () {}, + ), + QuickCreateButton( + text: 'Category 6', + onPressed: () {}, + ), + ], + ), + ], + ), ), ), + SizedBox(height: MediaQuery.paddingOf(context).bottom), ], ), ), @@ -215,7 +187,7 @@ class _HomeViewState extends State { /// Loads the data for the HomeView from the database. /// This includes the match count, group count, and recent matches. - void loadHomeViewData() { + Future loadHomeViewData() async { final db = Provider.of(context, listen: false); Future.wait([ db.matchDao.getMatchCount(), @@ -231,11 +203,6 @@ class _HomeViewState extends State { ..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; @@ -244,18 +211,15 @@ class _HomeViewState extends State { }); } - /// 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) { - final playerCount = game.players?.length ?? 0; - return loc.players_count(playerCount); + /// Updates the winner information for a specific match in the recent matches list. + Future updatedWinnerinRecentMatches(String matchId) async { + final db = Provider.of(context, listen: false); + final winner = await db.matchDao.getWinner(matchId: matchId); + final matchIndex = recentMatches.indexWhere((match) => match.id == matchId); + if (matchIndex != -1) { + setState(() { + recentMatches[matchIndex].winner = winner; + }); } - if (game.players == null || game.players!.isEmpty) { - return game.group!.name; - } - return '${game.group!.name} + ${game.players!.length}'; } } diff --git a/lib/presentation/views/main_menu/match_view/match_view.dart b/lib/presentation/views/main_menu/match_view/match_view.dart index 1781302..ecfa9ca 100644 --- a/lib/presentation/views/main_menu/match_view/match_view.dart +++ b/lib/presentation/views/main_menu/match_view/match_view.dart @@ -1,4 +1,5 @@ import 'dart:core' hide Match; + import 'package:flutter/material.dart'; import 'package:game_tracker/core/adaptive_page_route.dart'; import 'package:game_tracker/core/constants.dart'; @@ -77,20 +78,26 @@ class _MatchViewState extends State { height: MediaQuery.paddingOf(context).bottom - 20, ); } - return MatchTile( - onTap: () async { - Navigator.push( - context, - adaptivePageRoute( - fullscreenDialog: true, - builder: (context) => MatchResultView( - match: matches[index], - onWinnerChanged: loadGames, - ), - ), - ); - }, - match: matches[index], + return Center( + child: Padding( + padding: const EdgeInsets.only(bottom: 12.0), + child: MatchTile( + width: MediaQuery.sizeOf(context).width * 0.95, + onTap: () async { + Navigator.push( + context, + adaptivePageRoute( + fullscreenDialog: true, + builder: (context) => MatchResultView( + match: matches[index], + onWinnerChanged: loadGames, + ), + ), + ); + }, + match: matches[index], + ), + ), ); }, ), @@ -105,8 +112,9 @@ class _MatchViewState extends State { Navigator.push( context, adaptivePageRoute( - builder: (context) => - CreateMatchView(onWinnerChanged: loadGames)) + builder: (context) => + CreateMatchView(onWinnerChanged: loadGames), + ), ); }, ), diff --git a/lib/presentation/widgets/tiles/match_tile.dart b/lib/presentation/widgets/tiles/match_tile.dart index 55d81c3..88ae1f1 100644 --- a/lib/presentation/widgets/tiles/match_tile.dart +++ b/lib/presentation/widgets/tiles/match_tile.dart @@ -10,8 +10,16 @@ import 'package:intl/intl.dart'; /// creation date, associated group, winner, and players. /// - [match]: The match data to be displayed. /// - [onTap]: The callback invoked when the tile is tapped. +/// - [width]: Optional width for the tile. +/// - [compact]: Whether to display the tile in a compact mode class MatchTile extends StatefulWidget { - const MatchTile({super.key, required this.match, required this.onTap}); + const MatchTile({ + super.key, + required this.match, + required this.onTap, + this.width, + this.compact = false, + }); /// The match data to be displayed. final Match match; @@ -19,6 +27,12 @@ class MatchTile extends StatefulWidget { /// The callback invoked when the tile is tapped. final VoidCallback onTap; + /// Optional width for the tile. + final double? width; + + /// Whether to display the tile in a compact mode + final bool compact; + @override State createState() => _MatchTileState(); } @@ -41,13 +55,10 @@ class _MatchTileState extends State { return GestureDetector( onTap: widget.onTap, child: Container( - margin: CustomTheme.tileMargin, + margin: EdgeInsets.zero, + width: widget.width, padding: const EdgeInsets.all(12), - decoration: BoxDecoration( - color: CustomTheme.boxColor, - border: Border.all(color: CustomTheme.boxBorder), - borderRadius: BorderRadius.circular(12), - ), + decoration: CustomTheme.standardBoxDecoration, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -80,7 +91,22 @@ class _MatchTileState extends State { const SizedBox(width: 6), Expanded( child: Text( - group.name, + '${group.name}${widget.match.players != null ? ' + ${widget.match.players?.length}' : ''}', + style: const TextStyle(fontSize: 14, color: Colors.grey), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + const SizedBox(height: 12), + ] else if (widget.compact) ...[ + Row( + children: [ + const Icon(Icons.person, size: 16, color: Colors.grey), + const SizedBox(width: 6), + Expanded( + child: Text( + '${widget.match.players!.length} ${loc.players}', style: const TextStyle(fontSize: 14, color: Colors.grey), overflow: TextOverflow.ellipsis, ), @@ -127,9 +153,46 @@ class _MatchTileState extends State { ), ), const SizedBox(height: 12), + ] else ...[ + Container( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 12, + ), + decoration: BoxDecoration( + color: Colors.amber.withValues(alpha: 0.1), + borderRadius: BorderRadius.circular(8), + border: Border.all( + color: Colors.amber.withValues(alpha: 0.3), + width: 1, + ), + ), + child: Row( + children: [ + const Icon( + Icons.watch_later, + size: 20, + color: Colors.amber, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + loc.match_in_progress, + style: const TextStyle( + fontSize: 14, + fontWeight: FontWeight.w600, + color: CustomTheme.textColor, + ), + overflow: TextOverflow.ellipsis, + ), + ), + ], + ), + ), + const SizedBox(height: 12), ], - if (_allPlayers.isNotEmpty) ...[ + if (_allPlayers.isNotEmpty && widget.compact == false) ...[ Text( loc.players, style: const TextStyle( diff --git a/pubspec.yaml b/pubspec.yaml index 26253fd..7ace12f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: game_tracker description: "Game Tracking App for Card Games" publish_to: 'none' -version: 0.0.1+37 +version: 0.0.3+93 environment: sdk: ^3.8.1 @@ -9,11 +9,6 @@ environment: dependencies: flutter: sdk: flutter - material_symbols_icons: ^4.2815.1 - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 drift: ^2.27.0 drift_flutter: ^0.2.4 path_provider: ^2.1.5