diff --git a/data/lib/api/ball_score/ball_score_model.dart b/data/lib/api/ball_score/ball_score_model.dart index c951d664..0e588ba3 100644 --- a/data/lib/api/ball_score/ball_score_model.dart +++ b/data/lib/api/ball_score/ball_score_model.dart @@ -818,9 +818,9 @@ extension BallScoreList on List { .where( (element) => element.wicket_type != null && - (element.wicket_type == WicketType.retired || - element.wicket_type == WicketType.retiredHurt || - element.wicket_type == WicketType.timedOut), + element.wicket_type != WicketType.retired && + element.wicket_type != WicketType.retiredHurt && + element.wicket_type != WicketType.timedOut, ) .length; diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index 30254a5d..ea8f031c 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -128,6 +128,21 @@ class TournamentService { return keyStats.where((element) => element.player.isActive).toList(); } + Future getUserOwnedTournamentsCount(String userId) { + final currentPlayer = TournamentMember( + id: userId, + role: TournamentMemberRole.organizer, + ); + + final filter = Filter.or( + Filter(FireStoreConst.createdBy, isEqualTo: userId), + Filter(FireStoreConst.members, arrayContains: currentPlayer.toJson()), + ); + return _tournamentCollection.where(filter).count().get().then((snapshot) { + return snapshot.count ?? 0; + }).catchError((error, stack) => throw AppError.fromError(error, stack)); + } + Stream> streamActiveTournaments() { final currentDate = DateTime.now(); final past30DaysDate = currentDate.subtract(Duration(days: 30)); diff --git a/khelo/android/app/src/main/AndroidManifest.xml b/khelo/android/app/src/main/AndroidManifest.xml index cf3c5160..db9ec764 100644 --- a/khelo/android/app/src/main/AndroidManifest.xml +++ b/khelo/android/app/src/main/AndroidManifest.xml @@ -6,7 +6,7 @@ show(BuildContext context, bool allowCrop) { + static Future show( + BuildContext context, { + required bool allowCrop, + bool cropOriginal = false, + }) { HapticFeedback.mediumImpact(); return showModalBottomSheet( context: context, @@ -22,6 +26,7 @@ class ImagePickerSheet extends ConsumerWidget { builder: (context) { return ImagePickerSheet( allowCrop: allowCrop, + cropOriginal: cropOriginal, onPop: (String? imagePath) { context.pop(imagePath); }); @@ -30,9 +35,15 @@ class ImagePickerSheet extends ConsumerWidget { } final bool allowCrop; + final bool cropOriginal; final void Function(String? imagePath) onPop; - ImagePickerSheet({super.key, required this.allowCrop, required this.onPop}); + ImagePickerSheet({ + super.key, + required this.allowCrop, + required this.onPop, + this.cropOriginal = false, + }); final ImagePicker _picker = ImagePicker(); @@ -150,11 +161,19 @@ class ImagePickerSheet extends ConsumerWidget { toolbarWidgetColor: context.colorScheme.onPrimary, initAspectRatio: CropAspectRatioPreset.square, lockAspectRatio: true, - aspectRatioPresets: [CropAspectRatioPreset.square], + aspectRatioPresets: [ + cropOriginal + ? CropAspectRatioPreset.original + : CropAspectRatioPreset.square + ], ), IOSUiSettings( title: context.l10n.image_picker_crop_image_title, - aspectRatioPresets: [CropAspectRatioPreset.square], + aspectRatioPresets: [ + cropOriginal + ? CropAspectRatioPreset.original + : CropAspectRatioPreset.square + ], ), WebUiSettings(context: context), ], diff --git a/khelo/lib/ui/flow/home/components/match_item.dart b/khelo/lib/ui/flow/home/components/match_item.dart index 75853b71..465d73c5 100644 --- a/khelo/lib/ui/flow/home/components/match_item.dart +++ b/khelo/lib/ui/flow/home/components/match_item.dart @@ -24,7 +24,7 @@ class MatchItem extends StatelessWidget { onTap: () => AppRoute.matchDetailTab(matchId: match.id).push(context), child: MediaQuery.withNoTextScaling( child: Container( - width: context.mediaQuerySize.width * 0.83, + width: 360, padding: const EdgeInsets.all(16), margin: const EdgeInsets.all(8), decoration: BoxDecoration( diff --git a/khelo/lib/ui/flow/home/home_screen.dart b/khelo/lib/ui/flow/home/home_screen.dart index 87cdc18f..3c677fbc 100644 --- a/khelo/lib/ui/flow/home/home_screen.dart +++ b/khelo/lib/ui/flow/home/home_screen.dart @@ -95,7 +95,7 @@ class _HomeScreenState extends ConsumerState { ? _content(context, state) : SizedBox( height: context.mediaQuerySize.height / - (state.tournaments.isEmpty ? 0.5 : 2), + (state.tournaments.isEmpty ? 1.3 : 2), child: EmptyScreen( title: context.l10n.home_screen_no_matches_title, description: @@ -145,6 +145,7 @@ class _HomeScreenState extends ConsumerState { List tournaments, ) { return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ _header( context, diff --git a/khelo/lib/ui/flow/settings/edit_profile/edit_profile_screen.dart b/khelo/lib/ui/flow/settings/edit_profile/edit_profile_screen.dart index 2a7ec39f..235f8d10 100644 --- a/khelo/lib/ui/flow/settings/edit_profile/edit_profile_screen.dart +++ b/khelo/lib/ui/flow/settings/edit_profile/edit_profile_screen.dart @@ -78,8 +78,10 @@ class EditProfileScreen extends ConsumerWidget { filePath: state.filePath, isLoading: state.isImageUploading, onEditButtonTap: () async { - final imagePath = - await ImagePickerSheet.show(context, true); + final imagePath = await ImagePickerSheet.show( + context, + allowCrop: true, + ); if (imagePath != null) { notifier.onImageChange(imagePath); } diff --git a/khelo/lib/ui/flow/settings/edit_profile/edit_profile_view_model.dart b/khelo/lib/ui/flow/settings/edit_profile/edit_profile_view_model.dart index 4878ab7b..9fdfde96 100644 --- a/khelo/lib/ui/flow/settings/edit_profile/edit_profile_view_model.dart +++ b/khelo/lib/ui/flow/settings/edit_profile/edit_profile_view_model.dart @@ -6,6 +6,7 @@ import 'package:data/service/auth/auth_service.dart'; import 'package:data/service/file_upload/file_upload_service.dart'; import 'package:data/service/match/match_service.dart'; import 'package:data/service/team/team_service.dart'; +import 'package:data/service/tournament/tournament_service.dart'; import 'package:data/service/user/user_service.dart'; import 'package:data/storage/app_preferences.dart'; import 'package:data/storage/provider/preferences_provider.dart'; @@ -27,6 +28,7 @@ final editProfileStateProvider = StateNotifierProvider.autoDispose< ref.read(authServiceProvider), ref.read(teamServiceProvider), ref.read(matchServiceProvider), + ref.read(tournamentServiceProvider), ref.read(currentUserPod), ref.read(currentUserJsonPod.notifier), ); @@ -40,6 +42,7 @@ class EditProfileViewNotifier extends StateNotifier { final AuthService _authService; final TeamService _teamService; final MatchService _matchService; + final TournamentService _tournamentService; final PreferenceNotifier _userJsonController; EditProfileViewNotifier( @@ -48,6 +51,7 @@ class EditProfileViewNotifier extends StateNotifier { this._authService, this._teamService, this._matchService, + this._tournamentService, UserModel? user, this._userJsonController, ) : super(EditProfileState( @@ -190,11 +194,12 @@ class EditProfileViewNotifier extends StateNotifier { showTransferTeamsSheet: false, ); - final [matchCount, teamCount] = await Future.wait([ + final [matchCount, teamCount, tournamentCount] = await Future.wait([ _matchService.getUserOwnedMatchesCount(userId), - _teamService.getUserOwnedTeamsCount(userId) + _teamService.getUserOwnedTeamsCount(userId), + _tournamentService.getUserOwnedTournamentsCount(userId) ]); - if (teamCount == 0 && matchCount == 0) { + if (teamCount == 0 && matchCount == 0 && tournamentCount == 0) { state = state.copyWith(showDeleteConfirmationDialog: true); } else { state = state.copyWith(showTransferTeamsSheet: true); diff --git a/khelo/lib/ui/flow/team/add_team/add_team_screen.dart b/khelo/lib/ui/flow/team/add_team/add_team_screen.dart index acc4b5e7..a8f2e8a0 100644 --- a/khelo/lib/ui/flow/team/add_team/add_team_screen.dart +++ b/khelo/lib/ui/flow/team/add_team/add_team_screen.dart @@ -96,7 +96,9 @@ class _AddTeamScreenState extends ConsumerState { return Stack( children: [ ListView( - padding: context.mediaQueryPadding + const EdgeInsets.all(16) + BottomStickyOverlay.padding, + padding: context.mediaQueryPadding + + const EdgeInsets.all(16) + + BottomStickyOverlay.padding, children: [ ProfileImageAvatar( size: profileViewHeight, @@ -105,8 +107,10 @@ class _AddTeamScreenState extends ConsumerState { imageUrl: state.editTeam?.profile_img_url, filePath: state.filePath, onEditButtonTap: () async { - final imagePath = - await ImagePickerSheet.show(context, true); + final imagePath = await ImagePickerSheet.show( + context, + allowCrop: true, + ); if (imagePath != null) { notifier.onImageSelect(imagePath); } diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index d19cd96a..52dcf642 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -283,7 +283,11 @@ class _AddTournamentScreenState extends ConsumerState { } void _pickImage({bool isBanner = false}) async { - final imagePath = await ImagePickerSheet.show(context, true); + final imagePath = await ImagePickerSheet.show( + context, + allowCrop: true, + cropOriginal: isBanner, + ); if (imagePath != null) { notifier.onImageChange(imagePath: imagePath, isBanner: isBanner); } diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_points_table_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_points_table_tab.dart index f997473f..49c47b6d 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_points_table_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_points_table_tab.dart @@ -5,6 +5,8 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/string_extensions.dart'; +import 'package:khelo/ui/app_route.dart'; +import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; @@ -94,21 +96,24 @@ class TournamentDetailPointsTableTab extends ConsumerWidget { } DataCell _teamProfileDataCell(BuildContext context, TeamModel team) { - return DataCell(Row( - children: [ - ImageAvatar( - initial: team.name_initial ?? team.name.initials(limit: 1), - imageUrl: team.profile_img_url, - size: 32, - ), - const SizedBox(width: 16), - Text( - team.name_initial ?? team.name.initials(limit: 2), - style: AppTextStyle.subtitle2.copyWith( - color: context.colorScheme.textPrimary, + return DataCell(OnTapScale( + onTap: () => AppRoute.teamDetail(teamId: team.id).push(context), + child: Row( + children: [ + ImageAvatar( + initial: team.name_initial ?? team.name.initials(limit: 1), + imageUrl: team.profile_img_url, + size: 32, ), - ) - ], + const SizedBox(width: 16), + Text( + team.name_initial ?? team.name.initials(limit: 2), + style: AppTextStyle.subtitle2.copyWith( + color: context.colorScheme.textPrimary, + ), + ) + ], + ), )); } diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_stats_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_stats_tab.dart index 28ed3521..f0822ac4 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_stats_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_stats_tab.dart @@ -7,12 +7,14 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/chip_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; import '../../../../../components/empty_screen.dart'; import '../../../../../components/image_avatar.dart'; +import '../../../../app_route.dart'; import '../components/filter_tab_view.dart'; import '../tournament_detail_view_model.dart'; @@ -148,38 +150,41 @@ class TournamentDetailStatsTab extends ConsumerWidget { DataCell _playerProfileDataCell(BuildContext context, UserModel player) { return DataCell( - Row( - children: [ - ImageAvatar( - size: 40, - initial: player.nameInitial, - imageUrl: player.profile_img_url, - ), - const SizedBox(width: 16), - Flexible( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text( - player.name ?? '', - style: AppTextStyle.body1.copyWith( - color: context.colorScheme.textPrimary, - overflow: TextOverflow.ellipsis, - ), - ), - if (player.player_role != null) ...[ - const SizedBox(height: 4), + OnTapScale( + onTap: () => AppRoute.userDetail(userId: player.id).push(context), + child: Row( + children: [ + ImageAvatar( + size: 40, + initial: player.nameInitial, + imageUrl: player.profile_img_url, + ), + const SizedBox(width: 16), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.center, + children: [ Text( - player.player_role!.getString(context), - style: AppTextStyle.caption - .copyWith(color: context.colorScheme.textSecondary), - ) + player.name ?? '', + style: AppTextStyle.body1.copyWith( + color: context.colorScheme.textPrimary, + overflow: TextOverflow.ellipsis, + ), + ), + if (player.player_role != null) ...[ + const SizedBox(height: 4), + Text( + player.player_role!.getString(context), + style: AppTextStyle.caption + .copyWith(color: context.colorScheme.textSecondary), + ) + ], ], - ], + ), ), - ), - ], + ], + ), ), ); } diff --git a/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart index 4dab3f2e..4d02d614 100644 --- a/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart +++ b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart @@ -103,22 +103,23 @@ class TeamSelectionViewNotifier extends StateNotifier { void onTeamCellTap(TeamModel team) { state = state.copyWith(showSelectionError: false); - final playersCount = - (team.players.where((player) => player.user.isActive).toList()).length; - if (playersCount >= 2) { - final isAlreadySelected = - state.selectedTeams.map((e) => e.id).contains(team.id); - - final teams = state.selectedTeams.toList(); - if (isAlreadySelected) { - teams.removeWhere((e) => e.id == team.id); - } else { + + final isAlreadySelected = + state.selectedTeams.map((e) => e.id).contains(team.id); + + final teams = state.selectedTeams.toList(); + if (isAlreadySelected) { + teams.removeWhere((e) => e.id == team.id); + } else { + final playersCount = team.players.where((player) => player.user.isActive).length; + + if (playersCount >= 2) { teams.add(team); + } else { + state = state.copyWith(showSelectionError: true); } - state = state.copyWith(selectedTeams: teams); - } else { - state = state.copyWith(showSelectionError: true); } + state = state.copyWith(selectedTeams: teams); } List getSelectedTeamOfOtherUser() {