Skip to content

Commit

Permalink
Merge pull request #123 from canopas/Mayank/implement-tournament-matc…
Browse files Browse the repository at this point in the history
…hes-tab

Implement tournament matches tab
  • Loading branch information
cp-mayank authored Oct 25, 2024
2 parents 0b2b8b0 + 97b4f0d commit f962fb7
Show file tree
Hide file tree
Showing 9 changed files with 305 additions and 18 deletions.
18 changes: 17 additions & 1 deletion data/lib/service/tournament/tournament_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,10 @@ class TournamentService {
..sort((a, b) => b.value?.compareTo(a.value ?? 0) ?? 0);
}

Future<void> updateTeamIds(String tournamentId, List<String> teamIds) async {
Future<void> updateTeamIds(
String tournamentId,
List<String> teamIds,
) async {
try {
await _tournamentCollection
.doc(tournamentId)
Expand All @@ -155,4 +158,17 @@ class TournamentService {
throw AppError.fromError(error, stack);
}
}

Future<void> updateMatchIds(
String tournamentId,
List<String> matchIds,
) async {
try {
await _tournamentCollection
.doc(tournamentId)
.update({FireStoreConst.matchIds: matchIds});
} catch (error, stack) {
throw AppError.fromError(error, stack);
}
}
}
1 change: 1 addition & 0 deletions data/lib/utils/constant/firestore_constant.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ class FireStoreConst {

// tournament field const
static const String members = "members";
static const String matchIds = "match_ids";
}

class DataConfig {
Expand Down
6 changes: 6 additions & 0 deletions khelo/assets/locales/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,12 @@
"tournament_detail_teams_empty_description": "You haven't added any teams to this tournament yet. Please select or create teams to get started.",
"tournament_detail_teams_select_btn": "Add teams",

"tournament_detail_matches_empty_title": "Schedule Your Matches!",
"tournament_detail_matches_empty_description": "Create matches to kick off your tournament and get the games started!",
"tournament_detail_matches_add_btn": "Add matches",
"tournament_detail_matches_filter_by_teams_title": "Filter by teams",
"tournament_detail_matches_filter_all_teams_option": "All Teams",

"tournament_detail_key_stat_most_runs_title": "Most Runs",
"tournament_detail_key_stat_most_wickets_title": "Most Wickets",
"tournament_detail_key_stat_most_fours_title": "Most Fours",
Expand Down
4 changes: 3 additions & 1 deletion khelo/lib/components/match_detail_cell.dart
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ class MatchDetailCell extends StatelessWidget {
final bool showStatusTag;
final bool showActionButtons;
final VoidCallback? onActionTap;
final Color? backgroundColor;

const MatchDetailCell({
super.key,
required this.match,
required this.onTap,
this.showStatusTag = true,
this.showActionButtons = false,
this.backgroundColor,
this.onActionTap,
});

Expand All @@ -35,7 +37,7 @@ class MatchDetailCell extends StatelessWidget {
child: Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: context.colorScheme.containerLow,
color: backgroundColor ?? context.colorScheme.containerLow,
border: Border.all(color: context.colorScheme.outline),
borderRadius: BorderRadius.circular(16),
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import 'package:data/api/match/match_model.dart';
import 'package:data/api/team/team_model.dart';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_svg/svg.dart';
import 'package:go_router/go_router.dart';
import 'package:khelo/components/match_detail_cell.dart';
import 'package:khelo/domain/extensions/context_extensions.dart';
import 'package:khelo/ui/app_route.dart';
import 'package:style/button/bottom_sticky_overlay.dart';
import 'package:style/button/primary_button.dart';
import 'package:style/extensions/context_extensions.dart';
import 'package:style/text/app_text_style.dart';

import '../../../../../components/action_bottom_sheet.dart';
import '../../../../../components/empty_screen.dart';
import '../../../../../gen/assets.gen.dart';
import '../tournament_detail_view_model.dart';

class TournamentDetailMatchesTab extends ConsumerWidget {
final List<TeamModel> teams;
final List<MatchModel> filteredMatches;
final Function(String) onMatchFilter;
final Function(List<MatchModel>) onSelected;

const TournamentDetailMatchesTab({
super.key,
required this.teams,
required this.filteredMatches,
required this.onMatchFilter,
required this.onSelected,
});

@override
Widget build(BuildContext context, WidgetRef ref) {
final state = ref.watch(tournamentDetailStateProvider);
if (filteredMatches.isEmpty) {
return Stack(
children: [
EmptyScreen(
title: context.l10n.tournament_detail_matches_empty_title,
description:
context.l10n.tournament_detail_matches_empty_description,
isShowButton: false,
),
_stickyButton(context),
],
);
}

return ListView(
padding: context.mediaQueryPadding.copyWith(top: 0) +
EdgeInsets.all(16).copyWith(bottom: 24),
children: [
_filterView(context, state),
...filteredMatches.map(
(match) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: MatchDetailCell(
backgroundColor: context.colorScheme.surface,
match: match,
onTap: () =>
AppRoute.matchDetailTab(matchId: match.id).push(context),
),
);
},
),
],
);
}

Widget _filterView(BuildContext context, TournamentDetailState state) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
context.l10n.tournament_detail_matches_filter_by_teams_title,
style: AppTextStyle.header4.copyWith(
color: context.colorScheme.textPrimary,
),
),
TextButton.icon(
iconAlignment: IconAlignment.end,
onPressed: () => showFilterOptionSelectionSheet(
context,
matchFilter: state.matchFilter,
onTap: onMatchFilter,
),
label: Text(
state.matchFilter ??
context.l10n.tournament_detail_matches_filter_all_teams_option,
style: AppTextStyle.body2.copyWith(
color: context.colorScheme.primary,
),
),
icon: SvgPicture.asset(
Assets.images.icArrowDown,
height: 18,
width: 18,
colorFilter: ColorFilter.mode(
context.colorScheme.primary,
BlendMode.srcATop,
),
),
),
],
);
}

void showFilterOptionSelectionSheet(
BuildContext context, {
required Function(String) onTap,
String? matchFilter,
}) async {
final filterOptions = [
context.l10n.tournament_detail_matches_filter_all_teams_option,
...teams.map((e) => e.name)
];
final matchFiltered = matchFilter ?? filterOptions.first;
return await showActionBottomSheet(
context: context,
items: filterOptions
.map((option) => BottomSheetAction(
title: option,
enabled: matchFiltered != option,
child: _checkWidget(
context,
isShowCheck: matchFiltered == option,
),
onTap: () {
context.pop();
onTap(option);
},
))
.toList());
}

Widget? _checkWidget(
BuildContext context, {
required bool isShowCheck,
}) =>
isShowCheck
? SvgPicture.asset(
Assets.images.icCheck,
colorFilter: ColorFilter.mode(
context.colorScheme.primary,
BlendMode.srcATop,
),
)
: null;

Widget _stickyButton(BuildContext context) {
return BottomStickyOverlay(
child: PrimaryButton(
context.l10n.tournament_detail_matches_add_btn,
onPressed: () {
onSelected.call([]);
},
),
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,18 +261,15 @@ class _TournamentDetailOverviewTabState
),
const SizedBox(height: 8),
SizedBox(
height: 130,
child: ListView.separated(
shrinkWrap: true,
height: 136,
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) => _teamCellView(
context,
teams[index],
child: Row(
children:
teams.map((team) => _teamCellView(context, team)).toList(),
),
separatorBuilder: (context, index) => const SizedBox(width: 16),
itemCount: teams.length,
),
)
),
],
),
);
Expand All @@ -284,6 +281,7 @@ class _TournamentDetailOverviewTabState
child: Container(
width: 120,
padding: EdgeInsets.all(16),
margin: EdgeInsets.symmetric(horizontal: 8),
decoration: BoxDecoration(
color: context.colorScheme.surface,
borderRadius: BorderRadius.circular(16),
Expand All @@ -303,6 +301,7 @@ class _TournamentDetailOverviewTabState
color: context.colorScheme.textPrimary,
),
textAlign: TextAlign.center,
textScaler: TextScaler.noScaling,
),
)
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:khelo/components/image_avatar.dart';
import 'package:khelo/domain/extensions/context_extensions.dart';
import 'package:khelo/domain/formatter/date_formatter.dart';
import 'package:khelo/ui/flow/tournament/components/sliver_header_delegate.dart';
import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_matches_tab.dart';
import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart';
import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart';
import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart';
Expand Down Expand Up @@ -136,6 +137,12 @@ class _TournamentDetailScreenState
teams: state.tournament?.teams ?? [],
onSelected: notifier.onTeamsSelected,
),
TournamentDetailMatchesTab(
teams: state.tournament!.teams,
filteredMatches: state.filteredMatches,
onMatchFilter: notifier.onMatchFilter,
onSelected: notifier.onMatchesSelected,
),
],
),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:async';

import 'package:data/api/match/match_model.dart';
import 'package:data/api/team/team_model.dart';
import 'package:data/api/tournament/tournament_model.dart';
import 'package:data/service/tournament/tournament_service.dart';
Expand Down Expand Up @@ -40,6 +41,7 @@ class TournamentDetailStateViewNotifier
.streamTournamentById(_tournamentId!)
.listen((tournament) {
state = state.copyWith(tournament: tournament, loading: false);
onMatchFilter(null);
}, onError: (e) {
state = state.copyWith(error: e, loading: false);
debugPrint(
Expand All @@ -65,6 +67,43 @@ class TournamentDetailStateViewNotifier
}
}

void onMatchesSelected(List<MatchModel> matches) async {
if (state.tournament == null) return;
try {
final matchIds = matches.map((e) => e.id).toList();
await _tournamentService.updateMatchIds(state.tournament!.id, matchIds);
} catch (e) {
state = state.copyWith(actionError: e);
debugPrint(
"TournamentDetailStateViewNotifier: error while selecting matches -> $e");
}
}

void onMatchFilter(String? filter) {
if (state.tournament == null) return;

final matches = state.tournament!.matches;

if (filter == null) {
state = state.copyWith(filteredMatches: matches);
return;
}
final names = state.tournament!.teams.map((e) => e.name).toList();

if (names.contains(filter)) {
final filteredMatches = matches.where((match) {
return match.teams.any((team) => team.team.name == filter);
}).toList();

state = state.copyWith(
matchFilter: filter,
filteredMatches: filteredMatches,
);
} else {
state = state.copyWith(matchFilter: filter, filteredMatches: matches);
}
}

@override
void dispose() {
_tournamentSubscription?.cancel();
Expand All @@ -80,5 +119,7 @@ class TournamentDetailState with _$TournamentDetailState {
@Default(0) int selectedTab,
Object? error,
Object? actionError,
@Default(null) String? matchFilter,
@Default([]) List<MatchModel> filteredMatches,
}) = _TournamentDetailState;
}
Loading

0 comments on commit f962fb7

Please sign in to comment.