diff --git a/README.md b/README.md index c6e0f3f4..f43cffa1 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ Khelo is an open-source Flutter project written in Dart using Firestore database ## Features 🌟🌟 - **Profile Management**: Create and manage profiles for players, coaches, and team managers effortlessly. -- **Team Creation**: Form your dream team by adding players and managing team compositions with ease.. +- **Team Creation**: Form your dream team by adding players and managing team compositions with ease. - **Player Management**: Keep track of player details, including statistics, performance history, and personal information. - **Match Data Recording**: Record detailed match data, including scores, wickets, runs, and other custom metrics, providing a comprehensive overview of each game. - **Performance Tracking**: Monitor and analyze player performance over time, enabling informed decision-making and strategic planning. diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index 74bff3ce..7cb5d95d 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:data/api/match/match_model.dart'; import 'package:data/api/team/team_model.dart'; @@ -68,52 +70,45 @@ class MatchService { return matches; } - Future getMatchById(String id) async { - DocumentReference matchRef = _firestore.collection(_collectionName).doc(id); - DocumentSnapshot snapshot = await matchRef.get(); - - AddEditMatchRequest match = - AddEditMatchRequest.fromJson(snapshot.data() as Map); + Future> getCurrentUserPlayedMatches() async { + if (_currentUserId == null) { + return []; + } - List teams = await getTeamsList(match.teams); - List? umpires = await getUserListFromUserIds(match.umpire_ids); - List? commentators = - await getUserListFromUserIds(match.commentator_ids); - List? scorers = await getUserListFromUserIds(match.scorer_ids); + QuerySnapshot snapshot = await _firestore.collection(_collectionName).get(); + List matches = []; - UserModel? referee; - if (match.referee_id != null) { - referee = await _userService.getUserById(match.referee_id!); + for (QueryDocumentSnapshot mainDoc in snapshot.docs) { + Map mainDocData = mainDoc.data() as Map; + AddEditMatchRequest match = AddEditMatchRequest.fromJson(mainDocData); + if (match.teams + .map((e) => e.squad.map((e) => e.id).contains(_currentUserId)) + .contains(true) && + match.match_status == MatchStatus.finish) { + List teams = await getTeamsList(match.teams); + matches.add(MatchModel( + id: match.id, + teams: teams, + match_type: match.match_type, + number_of_over: match.number_of_over, + over_per_bowler: match.over_per_bowler, + city: match.city, + ground: match.ground, + start_time: match.start_time, + ball_type: match.ball_type, + pitch_type: match.pitch_type, + match_status: match.match_status, + toss_winner_id: match.toss_winner_id, + toss_decision: match.toss_decision, + current_playing_team_id: match.current_playing_team_id, + )); + } } - var matchModel = MatchModel( - id: match.id, - teams: teams, - match_type: match.match_type, - number_of_over: match.number_of_over, - over_per_bowler: match.over_per_bowler, - city: match.city, - ground: match.ground, - start_time: match.start_time, - ball_type: match.ball_type, - pitch_type: match.pitch_type, - match_status: match.match_status, - commentators: commentators, - referee: referee, - scorers: scorers, - umpires: umpires, - power_play_overs1: match.power_play_overs1 ?? [], - power_play_overs2: match.power_play_overs2 ?? [], - power_play_overs3: match.power_play_overs3 ?? [], - toss_decision: match.toss_decision, - toss_winner_id: match.toss_winner_id, - current_playing_team_id: match.current_playing_team_id, - ); - - return matchModel; + return matches; } - Future> getCurrentUserMatches() async { + Future> getCurrentUserRelatedMatches() async { if (_currentUserId == null) { return []; } @@ -124,11 +119,14 @@ class MatchService { for (QueryDocumentSnapshot mainDoc in snapshot.docs) { Map mainDocData = mainDoc.data() as Map; AddEditMatchRequest match = AddEditMatchRequest.fromJson(mainDocData); - if (match.teams - .map((e) => e.squad.map((e) => e.id).contains(_currentUserId)) - .contains(true) && - match.match_status == MatchStatus.finish) { - List teams = await getTeamsList(match.teams); + List teams = await getTeamsList(match.teams); + if (teams + .map((e) => + e.team.players?.map((e) => e.id).contains(_currentUserId)) + .contains(true) || + teams + .map((e) => e.team.created_by == _currentUserId) + .contains(true)) { matches.add(MatchModel( id: match.id, teams: teams, @@ -152,7 +150,8 @@ class MatchService { } Future> getMatchesByTeamId(String teamId) async { - CollectionReference matchCollection = _firestore.collection(_collectionName); + CollectionReference matchCollection = + _firestore.collection(_collectionName); QuerySnapshot mainCollectionSnapshot = await matchCollection.get(); @@ -185,6 +184,92 @@ class MatchService { return matches; } + Stream> getRunningMatches() { + StreamController> controller = + StreamController>(); + + _firestore + .collection(_collectionName) + .where('match_status', isEqualTo: 2) + .snapshots() + .listen((QuerySnapshot> snapshot) async { + List matches = []; + + for (QueryDocumentSnapshot mainDoc in snapshot.docs) { + Map mainDocData = + mainDoc.data() as Map; + AddEditMatchRequest match = AddEditMatchRequest.fromJson(mainDocData); + + List teams = await getTeamsList(match.teams); + matches.add(MatchModel( + id: match.id, + teams: teams, + match_type: match.match_type, + number_of_over: match.number_of_over, + over_per_bowler: match.over_per_bowler, + city: match.city, + ground: match.ground, + start_time: match.start_time, + ball_type: match.ball_type, + pitch_type: match.pitch_type, + match_status: match.match_status, + toss_winner_id: match.toss_winner_id, + toss_decision: match.toss_decision, + current_playing_team_id: match.current_playing_team_id, + )); + } + + controller.add(matches); + }); + + return controller.stream; + } + + Future getMatchById(String id) async { + DocumentReference matchRef = _firestore.collection(_collectionName).doc(id); + DocumentSnapshot snapshot = await matchRef.get(); + + AddEditMatchRequest match = + AddEditMatchRequest.fromJson(snapshot.data() as Map); + + List teams = await getTeamsList(match.teams); + List? umpires = await getUserListFromUserIds(match.umpire_ids); + List? commentators = + await getUserListFromUserIds(match.commentator_ids); + List? scorers = await getUserListFromUserIds(match.scorer_ids); + + UserModel? referee; + if (match.referee_id != null) { + referee = await _userService.getUserById(match.referee_id!); + } + + var matchModel = MatchModel( + id: match.id, + teams: teams, + match_type: match.match_type, + number_of_over: match.number_of_over, + over_per_bowler: match.over_per_bowler, + city: match.city, + ground: match.ground, + start_time: match.start_time, + ball_type: match.ball_type, + pitch_type: match.pitch_type, + match_status: match.match_status, + commentators: commentators, + referee: referee, + scorers: scorers, + umpires: umpires, + power_play_overs1: match.power_play_overs1 ?? [], + power_play_overs2: match.power_play_overs2 ?? [], + power_play_overs3: match.power_play_overs3 ?? [], + toss_decision: match.toss_decision, + toss_winner_id: match.toss_winner_id, + current_playing_team_id: match.current_playing_team_id, + ); + + return matchModel; + } + Future updateMatch(AddEditMatchRequest match) async { DocumentReference matchRef = _firestore.collection(_collectionName).doc(match.id); diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 780b4021..7e5a43e4 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -18,8 +18,10 @@ "common_next_title": "Next", "common_yes_title": "Yes", "common_no_title": "No", + "common_no_title": "No", + "common_wicket_taken_title" : " Wicket taken", "common_not_specified_title": "Not Specified", - "common_obscure_phone_number_text": "+{countryCode} ***** ***{lastDigits}", + "common_obscure_phone_number_text": "{countryCode} ***** ***{lastDigits}", "@common_obscure_phone_number_text": { "description": "+{countryCode} ***** ***{lastDigits}", "placeholders": { @@ -71,6 +73,8 @@ "edit_profile_gender_other_title": "Other", "edit_profile_save_title": "SAVE", + "home_screen_title": "Home", + "image_picker_choose_option_title": "Choose an option.", "image_picker_crop_image_title": "Crop Image", @@ -170,6 +174,7 @@ "add_match_start_match_title": "Start Match", "add_match_no_of_over_title": "no. of over", "add_match_over_per_bowler_title": "over per bowler", + "add_match_joined_on_title": "Joined on", "add_match_officials_screen_title": "Add Match Officials", "add_match_officials_add_officials_title": "Add Officials", @@ -178,6 +183,12 @@ "search_team_search_placeholder_title": "Search team name", "search_team_your_teams_title": "Your Teams", "search_team_select_title": "Select", + "search_team_member_title" : "{count, plural, =0{{count} Members} =1{{count} Member} other{{count} Members}}", + "@search_team_member_title": { + "placeholders": { + "count": {} + } + }, "search_user_hint_title": "Search user name", "search_user_empty_text": "Search the person name in above input area.", @@ -267,6 +278,7 @@ } }, "score_board_choose_fielder_title" : "choose Fielder", + "score_board_injured_tag_title" : "Injured", "score_board_who_on_strike_title" : "Who's on Strike?", "score_board_who_got_out_title" : "Who got out", "score_board_runs_dot_title" : " Runs.", @@ -319,6 +331,7 @@ "team_detail_run_rate_title": "Run Rate", "team_detail_match_played_title": "\nMatch played", "team_detail_matches_played_title": "\nMatches played", + "team_detail_overs_title": "Overs:", "match_status_yet_to_start_title" : "Yet to start", "match_status_running_title" : "Running", @@ -350,7 +363,6 @@ "my_stat_stats_strike_rate_title": "\nStrike rate", "my_stat_stats_ball_faced_title": " Ball faced", "my_stat_stats_bowling_statics_title": "Bowling Statistics", - "my_stat_stats_wicket_taken_title": "Wicket taken", "my_stat_stats_economy_rate_title": " Economy rate", "my_stat_stats_fielding_statics_title": "Fielding Statistics", "my_stat_stats_catches_title": "Catches", @@ -358,7 +370,6 @@ "my_stat_stats_stumping_title": " Stumping", "match_stat_screen_title" : "Match Stat", - "match_stat_wicket_taken_title" : " Wicket taken", "match_stat_in_over_text" : " (in {count} Over)", "@match_stat_in_over_text": { "description": " (in {count} Over)", diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart new file mode 100644 index 00000000..0fdf1943 --- /dev/null +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -0,0 +1,18 @@ +import 'package:flutter/cupertino.dart'; +import 'package:intl/intl.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; + +enum DateFormatType { dateAndTime, } + +extension DateFormatter on DateTime { + String format(BuildContext context, DateFormatType type) { + if (isUtc) return toLocal().format(context, type); + + switch (type) { + case DateFormatType.dateAndTime: + return DateFormat( + 'EEE, MMM dd yyyy ${context.is24HourFormat ? 'HH:mm' : 'hh:mm a'}') + .format(this); + } + } +} diff --git a/khelo/lib/domain/formatter/string_formatter.dart b/khelo/lib/domain/formatter/string_formatter.dart new file mode 100644 index 00000000..8a3415c6 --- /dev/null +++ b/khelo/lib/domain/formatter/string_formatter.dart @@ -0,0 +1,28 @@ +import 'package:flutter/cupertino.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; + +const _missing = '-'; + +extension StringExtension on String? { + String format(BuildContext context, StringFormats formatType) { + if (this == null) return _missing; + + switch (formatType) { + case StringFormats.obscurePhoneNumber: + if (this!.length < 13) { + return _missing; + } + return context.l10n.common_obscure_phone_number_text( + this!.substring(0, 3), + this!.substring(this!.length - 2)); // TODO: change to 1 if needed + } + } +} + +enum StringFormats { + obscurePhoneNumber('+## ***** ***##'); + + const StringFormats(this.value); + + final String value; +} diff --git a/khelo/lib/ui/app.dart b/khelo/lib/ui/app.dart index 6806ae18..43fcbe64 100644 --- a/khelo/lib/ui/app.dart +++ b/khelo/lib/ui/app.dart @@ -79,4 +79,4 @@ class _AppState extends ConsumerState { }, )); } -} +} \ No newline at end of file diff --git a/khelo/lib/ui/flow/home/home_screen.dart b/khelo/lib/ui/flow/home/home_screen.dart index 048f4149..3cc43585 100644 --- a/khelo/lib/ui/flow/home/home_screen.dart +++ b/khelo/lib/ui/flow/home/home_screen.dart @@ -1,23 +1,175 @@ -import 'package:flutter/cupertino.dart'; +import 'package:data/api/match/match_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/app_page.dart'; +import 'package:khelo/components/image_avatar.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; +import 'package:khelo/ui/flow/home/home_view_model.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_style.dart'; -class HomeScreen extends StatefulWidget { +class HomeScreen extends ConsumerWidget { const HomeScreen({super.key}); @override - State createState() => _HomeScreenState(); -} + Widget build(BuildContext context, WidgetRef ref) { + final state = ref.watch(homeViewStateProvider); -class _HomeScreenState extends State { - @override - Widget build(BuildContext context) { - return Center( - child: Text( - "Home", - style: - AppTextStyle.header4.copyWith(color: context.colorScheme.primary), + return AppPage( + title: context.l10n.home_screen_title, + body: _body(context, state), + ); + } + + Widget _body( + BuildContext context, + HomeViewState state, + ) { + if (state.loading) { + return const Center(child: AppProgressIndicator()); + } + + return ListView( + padding: context.mediaQueryPadding, + children: [ + _matchCardSlider(context, state), + ], + ); + } + + Widget _matchCardSlider( + BuildContext context, + HomeViewState state, + ) { + return SizedBox( + height: 220, + child: state.matches.length == 1 + ? Padding( + padding: const EdgeInsets.symmetric(horizontal: 8.0), + child: _matchCell(context, state.matches.first), + ) + : PageView.builder( + controller: PageController( + viewportFraction: 0.9, + initialPage: state.matches.length * 100, + ), + itemBuilder: (BuildContext context, int index) { + return _matchCell(context, + state.matches[(index % state.matches.length).toInt()]); + }, + ), + ); + } + + Widget _matchCell(BuildContext context, MatchModel match) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + margin: const EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + border: Border.all(color: context.colorScheme.outline), + borderRadius: BorderRadius.circular(20), + color: context.colorScheme.containerLow, + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: _teamNameView( + context, + match.teams.first, + match.current_playing_team_id == + match.teams.first.team.id)), + VerticalDivider( + color: context.colorScheme.outline, + thickness: 2, + ), + Expanded( + child: _teamNameView( + context, + match.teams.last, + match.current_playing_team_id == + match.teams.last.team.id)), + ], + ), + ), + _matchDetailView(context, match), + ], ), ); } + + Widget _teamNameView( + BuildContext context, + MatchTeamModel team, + bool isCurrentlyPlaying, + ) { + return Column( + children: [ + ImageAvatar( + initial: team.team.name[0].toUpperCase(), + imageUrl: team.team.profile_img_url, + size: 50, + ), + const SizedBox( + height: 4, + ), + Text( + team.team.name, + overflow: TextOverflow.ellipsis, + textAlign: TextAlign.center, + style: AppTextStyle.header4.copyWith( + color: isCurrentlyPlaying + ? context.colorScheme.primary + : context.colorScheme.textPrimary), + maxLines: 3, + ), + const SizedBox( + height: 4, + ), + Text.rich(TextSpan( + text: (team.run ?? 0).toString(), + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary, fontSize: 24), + children: [ + TextSpan( + text: " (${((team.over ?? 1) - 1).toStringAsFixed(1)})", + style: AppTextStyle.body1 + .copyWith(color: context.colorScheme.textPrimary)) + ])), + Text("${context.l10n.common_wicket_taken_title}: ${team.wicket ?? 0}", + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary)), + ], + ); + } + + Widget _matchDetailView( + BuildContext context, + MatchModel match, + ) { + return Column( + children: [ + Divider( + color: context.colorScheme.outline, + thickness: 2, + ), + Text( + "${match.ground} - ${match.match_type.getString(context)} - ${context.l10n.match_list_overs_title(match.number_of_over)}", + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + Text(match.start_time.format(context, DateFormatType.dateAndTime), + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary)), + ], + ); + } } diff --git a/khelo/lib/ui/flow/home/home_view_model.dart b/khelo/lib/ui/flow/home/home_view_model.dart new file mode 100644 index 00000000..41be2cd0 --- /dev/null +++ b/khelo/lib/ui/flow/home/home_view_model.dart @@ -0,0 +1,43 @@ +import 'package:data/api/match/match_model.dart'; +import 'package:data/service/match/match_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'home_view_model.freezed.dart'; + +final homeViewStateProvider = + StateNotifierProvider.autoDispose( + (ref) => HomeViewNotifier(ref.read(matchServiceProvider)), +); + +class HomeViewNotifier extends StateNotifier { + final MatchService _matchService; + + HomeViewNotifier(this._matchService) : super(const HomeViewState()) { + loadMatches(); + } + + void loadMatches() async { + state = state.copyWith(loading: state.matches.isEmpty); + + _matchService.getRunningMatches().listen( + (matches) { + state = state.copyWith(matches: matches, loading: false, error: null); + }, + onError: (e) { + state = state.copyWith(error: e, loading: false); + debugPrint("HomeViewNotifier: error while load matches -> $e"); + }, + ); + } +} + +@freezed +class HomeViewState with _$HomeViewState { + const factory HomeViewState({ + Object? error, + @Default(false) bool loading, + @Default([]) List matches, + }) = _HomeViewState; +} diff --git a/khelo/lib/ui/flow/home/home_view_model.freezed.dart b/khelo/lib/ui/flow/home/home_view_model.freezed.dart new file mode 100644 index 00000000..c2af0669 --- /dev/null +++ b/khelo/lib/ui/flow/home/home_view_model.freezed.dart @@ -0,0 +1,176 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'home_view_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +T _$identity(T value) => value; + +final _privateConstructorUsedError = UnsupportedError( + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); + +/// @nodoc +mixin _$HomeViewState { + Object? get error => throw _privateConstructorUsedError; + bool get loading => throw _privateConstructorUsedError; + List get matches => throw _privateConstructorUsedError; + + @JsonKey(ignore: true) + $HomeViewStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $HomeViewStateCopyWith<$Res> { + factory $HomeViewStateCopyWith( + HomeViewState value, $Res Function(HomeViewState) then) = + _$HomeViewStateCopyWithImpl<$Res, HomeViewState>; + @useResult + $Res call({Object? error, bool loading, List matches}); +} + +/// @nodoc +class _$HomeViewStateCopyWithImpl<$Res, $Val extends HomeViewState> + implements $HomeViewStateCopyWith<$Res> { + _$HomeViewStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? loading = null, + Object? matches = null, + }) { + return _then(_value.copyWith( + error: freezed == error ? _value.error : error, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + matches: null == matches + ? _value.matches + : matches // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$HomeViewStateImplCopyWith<$Res> + implements $HomeViewStateCopyWith<$Res> { + factory _$$HomeViewStateImplCopyWith( + _$HomeViewStateImpl value, $Res Function(_$HomeViewStateImpl) then) = + __$$HomeViewStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({Object? error, bool loading, List matches}); +} + +/// @nodoc +class __$$HomeViewStateImplCopyWithImpl<$Res> + extends _$HomeViewStateCopyWithImpl<$Res, _$HomeViewStateImpl> + implements _$$HomeViewStateImplCopyWith<$Res> { + __$$HomeViewStateImplCopyWithImpl( + _$HomeViewStateImpl _value, $Res Function(_$HomeViewStateImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? loading = null, + Object? matches = null, + }) { + return _then(_$HomeViewStateImpl( + error: freezed == error ? _value.error : error, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + matches: null == matches + ? _value._matches + : matches // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +class _$HomeViewStateImpl implements _HomeViewState { + const _$HomeViewStateImpl( + {this.error, + this.loading = false, + final List matches = const []}) + : _matches = matches; + + @override + final Object? error; + @override + @JsonKey() + final bool loading; + final List _matches; + @override + @JsonKey() + List get matches { + if (_matches is EqualUnmodifiableListView) return _matches; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_matches); + } + + @override + String toString() { + return 'HomeViewState(error: $error, loading: $loading, matches: $matches)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$HomeViewStateImpl && + const DeepCollectionEquality().equals(other.error, error) && + (identical(other.loading, loading) || other.loading == loading) && + const DeepCollectionEquality().equals(other._matches, _matches)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(error), + loading, + const DeepCollectionEquality().hash(_matches)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$HomeViewStateImplCopyWith<_$HomeViewStateImpl> get copyWith => + __$$HomeViewStateImplCopyWithImpl<_$HomeViewStateImpl>(this, _$identity); +} + +abstract class _HomeViewState implements HomeViewState { + const factory _HomeViewState( + {final Object? error, + final bool loading, + final List matches}) = _$HomeViewStateImpl; + + @override + Object? get error; + @override + bool get loading; + @override + List get matches; + @override + @JsonKey(ignore: true) + _$$HomeViewStateImplCopyWith<_$HomeViewStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/khelo/lib/ui/flow/intro/intro_screen.dart b/khelo/lib/ui/flow/intro/intro_screen.dart index 5a43e112..4375994a 100644 --- a/khelo/lib/ui/flow/intro/intro_screen.dart +++ b/khelo/lib/ui/flow/intro/intro_screen.dart @@ -21,7 +21,7 @@ class _IntroScreenState extends ConsumerState { return AppPage( body: Builder(builder: (context) { return Padding( - padding: MediaQuery.of(context).padding + + padding: context.mediaQueryPadding + const EdgeInsets.symmetric(horizontal: 16.0), child: SizedBox( width: double.infinity, @@ -49,9 +49,7 @@ class _IntroScreenState extends ConsumerState { ), PrimaryButton( context.l10n.intro_sign_in_btn_text, - onPressed: () async { - await AppRoute.phoneLogin.push(context); - }, + onPressed: () => AppRoute.phoneLogin.push(context), ), ], ), diff --git a/khelo/lib/ui/flow/main/main_screen.dart b/khelo/lib/ui/flow/main/main_screen.dart index 0147049c..6e5f0aac 100644 --- a/khelo/lib/ui/flow/main/main_screen.dart +++ b/khelo/lib/ui/flow/main/main_screen.dart @@ -13,9 +13,7 @@ import 'package:style/navigation/bottom_navigation_bar.dart'; import '../home/home_screen.dart'; class MainScreen extends ConsumerStatefulWidget { - final String? communityShareId; - - const MainScreen({super.key, this.communityShareId}); + const MainScreen({super.key}); @override ConsumerState createState() => _MainScreenState(); diff --git a/khelo/lib/ui/flow/matches/add_match/add_match_screen.dart b/khelo/lib/ui/flow/matches/add_match/add_match_screen.dart index 86ebf3e7..6423111f 100644 --- a/khelo/lib/ui/flow/matches/add_match/add_match_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/add_match_screen.dart @@ -4,11 +4,11 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; -import 'package:intl/intl.dart'; import 'package:khelo/components/app_page.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/domain/extensions/widget_extension.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/matches/add_match/add_match_view_model.dart'; import 'package:khelo/ui/flow/matches/add_match/match_officials/add_match_officials_view_model.dart'; @@ -117,7 +117,10 @@ class _AddMatchScreenState extends ConsumerState { AddMatchViewState state, ) { return IconButton( - onPressed: () => _showDeleteAlert(context, () => notifier.deleteMatch()), + onPressed: () => _showDeleteAlert( + context, + onDelete: () => notifier.deleteMatch(), + ), icon: const Icon( Icons.delete_outline, ), @@ -200,8 +203,12 @@ class _AddMatchScreenState extends ConsumerState { ); } - Widget _selectTeamCell(BuildContext context, AddMatchViewNotifier notifier, - AddMatchViewState state, TeamType type) { + Widget _selectTeamCell( + BuildContext context, + AddMatchViewNotifier notifier, + AddMatchViewState state, + TeamType type, + ) { final TeamModel? team = type == TeamType.a ? state.teamA : state.teamB; return Column( children: [ @@ -267,8 +274,13 @@ class _AddMatchScreenState extends ConsumerState { ); } - Widget _selectSquadButton(BuildContext context, AddMatchViewNotifier notifier, - AddMatchViewState state, TeamModel team, TeamType type) { + Widget _selectSquadButton( + BuildContext context, + AddMatchViewNotifier notifier, + AddMatchViewState state, + TeamModel team, + TeamType type, + ) { return OnTapScale( onTap: () async { final squad = await AppRoute.selectSquad( @@ -328,15 +340,15 @@ class _AddMatchScreenState extends ConsumerState { onTap: () => _selectDateTime(context, notifier, state), child: Text.rich(TextSpan(children: [ WidgetSpan( - child: Padding( - padding: const EdgeInsets.only(right: 8.0), - child: Icon(Icons.calendar_today, - color: context.colorScheme.textPrimary), - )), + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: Icon(Icons.calendar_today, + color: context.colorScheme.textPrimary), + ), + ), TextSpan( - text: DateFormat( - 'EEE, MMM dd yyyy ${context.is24HourFormat ? 'HH:mm' : 'hh:mm a'}') - .format(state.matchTime), + text: state.matchTime + .format(context, DateFormatType.dateAndTime), style: AppTextStyle.subtitle1.copyWith( color: context.colorScheme.textSecondary, fontSize: 22)), ])), @@ -691,8 +703,11 @@ class _AddMatchScreenState extends ConsumerState { ); } - Widget _stickyButton(BuildContext context, AddMatchViewNotifier notifier, - AddMatchViewState state) { + Widget _stickyButton( + BuildContext context, + AddMatchViewNotifier notifier, + AddMatchViewState state, + ) { return BottomStickyOverlay( child: PrimaryButton( context.l10n.add_match_start_match_title, @@ -703,8 +718,11 @@ class _AddMatchScreenState extends ConsumerState { ); } - Future _selectDateTime(BuildContext context, - AddMatchViewNotifier notifier, AddMatchViewState state) async { + Future _selectDateTime( + BuildContext context, + AddMatchViewNotifier notifier, + AddMatchViewState state, + ) async { showDatePicker( context: context, initialDate: state.matchTime, @@ -742,8 +760,11 @@ class _AddMatchScreenState extends ConsumerState { } } - Widget _overDetail(BuildContext context, AddMatchViewNotifier notifier, - AddMatchViewState state) { + Widget _overDetail( + BuildContext context, + AddMatchViewNotifier notifier, + AddMatchViewState state, + ) { return Row( children: [ Expanded( @@ -768,7 +789,10 @@ class _AddMatchScreenState extends ConsumerState { ); } - void _showDeleteAlert(BuildContext context, Function() onDelete) { + void _showDeleteAlert( + BuildContext context, { + required Function() onDelete, + }) { showAdaptiveDialog( context: context, builder: (context) { diff --git a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart index 25eabde4..024ee632 100644 --- a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart +++ b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.dart @@ -298,6 +298,9 @@ class AddMatchViewState with _$AddMatchViewState { String? teamAAdminId, String? teamBAdminId, @Default([]) List officials, + @Default([]) List? firstPowerPlay, + @Default([]) List? secondPowerPlay, + @Default([]) List? thirdPowerPlay, @Default(PitchType.rough) PitchType pitchType, @Default(MatchType.limitedOvers) MatchType matchType, @Default(BallType.leather) BallType ballType, @@ -308,9 +311,6 @@ class AddMatchViewState with _$AddMatchViewState { @Default(false) bool isAddMatchInProgress, @Default(null) bool? pushTossDetailScreen, @Default(null) bool? pop, - @Default([]) List? firstPowerPlay, - @Default([]) List? secondPowerPlay, - @Default([]) List? thirdPowerPlay, }) = _AddMatchViewState; } diff --git a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.freezed.dart b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.freezed.dart index 2c1704e7..f32829b9 100644 --- a/khelo/lib/ui/flow/matches/add_match/add_match_view_model.freezed.dart +++ b/khelo/lib/ui/flow/matches/add_match/add_match_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'add_match_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$AddMatchViewState { diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart index e9138d4b..d4aea1f9 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_screen.dart @@ -66,7 +66,9 @@ class _AddMatchOfficialsScreenState } List _getFilteredUser( - AddMatchOfficialsState state, MatchOfficials type) { + AddMatchOfficialsState state, + MatchOfficials type, + ) { return state.officials .where((element) => element.type == type) .map((e) => e.user) @@ -96,10 +98,11 @@ class _AddMatchOfficialsScreenState } Widget _officialList( - BuildContext context, - AddMatchOfficialsViewNotifier notifier, - AddMatchOfficialsState state, - MatchOfficials type) { + BuildContext context, + AddMatchOfficialsViewNotifier notifier, + AddMatchOfficialsState state, + MatchOfficials type, + ) { final users = _getFilteredUser(state, type); return SingleChildScrollView( scrollDirection: Axis.horizontal, @@ -197,9 +200,7 @@ class _AddMatchOfficialsScreenState context.l10n.add_match_officials_add_officials_title, enabled: true, progress: false, - onPressed: () { - context.pop(state.officials); - }, + onPressed: () => context.pop(state.officials), ), ); } diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_view_model.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_view_model.dart index 16a3c815..4ae946a2 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_view_model.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/add_match_officials_view_model.dart @@ -24,8 +24,9 @@ class AddMatchOfficialsViewNotifier if (isAlreadyAdded(type, user.id)) { return; } - state = - state.copyWith(officials: [...state.officials, Officials(type, user)]); + state = state.copyWith( + officials: [...state.officials, Officials(type, user)], + ); } void removeOfficial(MatchOfficials type, UserModel user) { diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_screen.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_screen.dart index eabd7426..49eb8f10 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_screen.dart @@ -5,7 +5,9 @@ import 'package:go_router/go_router.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/flow/matches/add_match/match_officials/components/search_user_view_model.dart'; +import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart'; import 'package:khelo/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -16,7 +18,6 @@ class SearchUserBottomSheet extends ConsumerWidget { return showModalBottomSheet( context: context, isScrollControlled: true, - isDismissible: true, showDragHandle: true, useRootNavigator: true, backgroundColor: context.colorScheme.surface, @@ -57,14 +58,14 @@ class SearchUserBottomSheet extends ConsumerWidget { context.l10n.search_user_empty_text, textAlign: TextAlign.center, style: AppTextStyle.subtitle1.copyWith( - color: context.colorScheme.textDisabled, fontSize: 20), + color: context.colorScheme.textDisabled, + fontSize: 20, + ), ), ) : ListView.separated( separatorBuilder: (context, index) { - return const SizedBox( - height: 16, - ); + return const SizedBox(height: 16); }, itemCount: state.searchedUsers.length, itemBuilder: (context, index) { @@ -116,12 +117,14 @@ class SearchUserBottomSheet extends ConsumerWidget { ); } - Widget _userProfileCell(BuildContext context, SearchUserViewNotifier notifier, - SearchUserViewState state, UserModel user) { + Widget _userProfileCell( + BuildContext context, + SearchUserViewNotifier notifier, + SearchUserViewState state, + UserModel user, + ) { return GestureDetector( - onTap: () { - // TODO: show userDetail Sheet - }, + onTap: () => UserDetailSheet.show(context, user), child: Row( children: [ ImageAvatar( @@ -152,9 +155,8 @@ class SearchUserBottomSheet extends ConsumerWidget { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - user.phone!.substring(1, 3), - user.phone!.substring(user.phone!.length - 2)), + user.phone + .format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), diff --git a/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_view_model.dart b/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_view_model.dart index fc4dc07e..98ac83f1 100644 --- a/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_view_model.dart +++ b/khelo/lib/ui/flow/matches/add_match/match_officials/components/search_user_view_model.dart @@ -54,8 +54,8 @@ class SearchUserViewNotifier extends StateNotifier { class SearchUserViewState with _$SearchUserViewState { const factory SearchUserViewState({ required TextEditingController searchController, - @Default([]) List searchedUsers, - UserModel? selectedUser, Object? error, + UserModel? selectedUser, + @Default([]) List searchedUsers, }) = _SearchUserViewState; } diff --git a/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart b/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart index 31720b8c..e9b9b630 100644 --- a/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/power_play/power_play_screen.dart @@ -11,242 +11,19 @@ import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; -// class PowerPlayScreen extends ConsumerWidget { -// final int totalOvers; -// final List? firstPowerPlay; -// final List? secondPowerPlay; -// final List? thirdPowerPlay; -// -// const PowerPlayScreen( -// {super.key, -// required this.totalOvers, -// this.firstPowerPlay, -// this.secondPowerPlay, -// this.thirdPowerPlay}); -// -// final double widgetWidth = 50; -// final double padding = 16.0; -// final double spacingBetweenElements = 8.0; -// -// @override -// Widget build(BuildContext context, WidgetRef ref) { -// int maxOversInRow = _calculateMaxOversInRow(context); -// double approxCellWidth = _calculateCellWidth(context, maxOversInRow); -// -// final notifier = ref.watch(powerPlayStateProvider.notifier); -// final state = ref.watch(powerPlayStateProvider); -// -// return AppPage( -// title: "Power Play Overs", -// body: Stack( -// children: [ -// Padding( -// padding: context.mediaQueryPadding + -// EdgeInsets.symmetric(horizontal: padding) + -// BottomStickyOverlay.padding, -// child: SingleChildScrollView( -// child: Column( -// children: [ -// for (final type in PowerPlayType.values) ...[ -// _overGridView( -// context: context, -// notifier: notifier, -// state: state, -// type: type, -// maxOversInRow: maxOversInRow, -// approxCellWidth: approxCellWidth, -// ), -// ], -// const SizedBox( -// height: 50, -// ), -// ], -// ), -// ), -// ), -// _stickyButton(context, state) -// ], -// ), -// ); -// } -// -// Widget _sectionTitle({ -// required BuildContext context, -// required String title, -// required bool isEnable, -// required Function() onResetTap, -// }) { -// return Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// const SizedBox( -// height: 24, -// ), -// Row( -// mainAxisAlignment: MainAxisAlignment.spaceBetween, -// children: [ -// Text( -// title, -// style: AppTextStyle.header1 -// .copyWith(color: context.colorScheme.textSecondary), -// ), -// TextButton( -// onPressed: isEnable ? () => onResetTap() : null, -// child: Text( -// "Reset", -// style: AppTextStyle.header4.copyWith( -// color: isEnable -// ? context.colorScheme.primary -// : context.colorScheme.textDisabled), -// )), -// ], -// ), -// const SizedBox( -// height: 16, -// ), -// ], -// ); -// } -// -// Widget _overGridView({ -// required BuildContext context, -// required PowerPlayViewNotifier notifier, -// required PowerPlayViewState state, -// required PowerPlayType type, -// required int maxOversInRow, -// required double approxCellWidth, -// }) { -// return Column( -// crossAxisAlignment: CrossAxisAlignment.start, -// children: [ -// _sectionTitle( -// context: context, -// title: type.getTitle(context), -// isEnable: true, -// onResetTap: () => notifier.onResetButtonTap(type), -// ), -// for (int i = 0; i < (totalOvers / maxOversInRow).ceil(); i++) ...[ -// if (i != 0) -// SizedBox( -// height: padding, -// ), -// Row( -// children: [ -// for (int j = 0; j < maxOversInRow; j++) ...[ -// if (i * maxOversInRow + j < totalOvers) ...[ -// SizedBox( -// width: j != 0 ? spacingBetweenElements : 0, -// ), -// _gridCellView( -// context: context, -// state: state, -// type: type, -// cellNumber: (i * maxOversInRow + j) + 1, -// approxCellWidth: approxCellWidth, -// onTap: (over) => notifier.onOverTap(over, type), -// ) -// ], -// ], -// ], -// ), -// ], -// ], -// ); -// } -// -// Widget _gridCellView({ -// required BuildContext context, -// required PowerPlayViewState state, -// required PowerPlayType type, -// required int cellNumber, -// required double approxCellWidth, -// required Function(int) onTap, -// }) { -// List overList = type == PowerPlayType.one -// ? state.firstPowerPlay -// : type == PowerPlayType.two -// ? state.secondPowerPlay -// : state.thirdPowerPlay; -// -// bool isSelected = overList.contains(cellNumber); -// -// bool isEnabled = !(type == PowerPlayType.one -// ? state.secondPowerPlay.contains(cellNumber) || -// state.thirdPowerPlay.contains(cellNumber) -// : type == PowerPlayType.two -// ? state.firstPowerPlay.contains(cellNumber) || -// state.thirdPowerPlay.contains(cellNumber) -// : state.secondPowerPlay.contains(cellNumber) || -// state.firstPowerPlay.contains(cellNumber)); -// return OnTapScale( -// onTap: isEnabled ? () => onTap(cellNumber) : null, -// child: Container( -// height: approxCellWidth, -// width: approxCellWidth, -// alignment: Alignment.center, -// decoration: BoxDecoration( -// color: isEnabled -// ? isSelected -// ? context.colorScheme.primary -// : context.colorScheme.containerLowOnSurface -// : context.colorScheme.surface, -// border: Border.all( -// color: context.colorScheme.containerNormalOnSurface), -// shape: BoxShape.circle), -// child: Text( -// "$cellNumber", -// style: AppTextStyle.subtitle1.copyWith( -// color: isEnabled -// ? context.colorScheme.textPrimary -// : context.colorScheme.textDisabled), -// )), -// ); -// } -// -// int _calculateMaxOversInRow(BuildContext context) { -// double screenWidth = MediaQuery.of(context).size.width - -// (context.mediaQueryPadding.horizontal + (padding * 2)); -// -// int maxOversInRow = -// (screenWidth / (widgetWidth + spacingBetweenElements)).floor(); -// return maxOversInRow > 0 ? maxOversInRow : 1; -// } -// -// double _calculateCellWidth(BuildContext context, int maxOversInRow) { -// double screenWidth = MediaQuery.of(context).size.width - -// (context.mediaQueryPadding.horizontal + (padding * 2)); -// double internalPadding = ((maxOversInRow - 1) * spacingBetweenElements); -// return ((screenWidth - internalPadding) / maxOversInRow); -// } -// -// Widget _stickyButton(BuildContext context, PowerPlayViewState state) { -// return BottomStickyOverlay( -// child: PrimaryButton( -// "okay", -// onPressed: () { -// context.pop([ -// state.firstPowerPlay, -// state.secondPowerPlay, -// state.thirdPowerPlay -// ]); -// }, -// ), -// ); -// } -// } - class PowerPlayScreen extends ConsumerStatefulWidget { final int totalOvers; final List? firstPowerPlay; final List? secondPowerPlay; final List? thirdPowerPlay; - const PowerPlayScreen( - {super.key, - required this.totalOvers, - this.firstPowerPlay, - this.secondPowerPlay, - this.thirdPowerPlay}); + const PowerPlayScreen({ + super.key, + required this.totalOvers, + this.firstPowerPlay, + this.secondPowerPlay, + this.thirdPowerPlay, + }); @override ConsumerState createState() => _PowerPlayScreenState(); @@ -264,7 +41,10 @@ class _PowerPlayScreenState extends ConsumerState { super.initState(); notifier = ref.read(powerPlayStateProvider.notifier); runPostFrame(() => notifier.setData( - widget.firstPowerPlay, widget.secondPowerPlay, widget.thirdPowerPlay)); + widget.firstPowerPlay, + widget.secondPowerPlay, + widget.thirdPowerPlay, + )); } @override @@ -464,13 +244,11 @@ class _PowerPlayScreenState extends ConsumerState { return BottomStickyOverlay( child: PrimaryButton( context.l10n.common_okay_title, - onPressed: () { - context.pop([ - state.firstPowerPlay, - state.secondPowerPlay, - state.thirdPowerPlay - ]); - }, + onPressed: () => context.pop([ + state.firstPowerPlay, + state.secondPowerPlay, + state.thirdPowerPlay + ]), ), ); } diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/components/select_admin_and_captain_dialog.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/components/select_admin_and_captain_dialog.dart index 761d3798..11de757b 100644 --- a/khelo/lib/ui/flow/matches/add_match/select_squad/components/select_admin_and_captain_dialog.dart +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/components/select_admin_and_captain_dialog.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/flow/matches/add_match/select_squad/select_squad_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -29,7 +30,11 @@ class SelectAdminAndCaptainDialog extends ConsumerWidget { return AlertDialog( backgroundColor: context.colorScheme.containerLowOnSurface, - title: Text(context.l10n.select_squad_select_admin_captain_title), + title: Text( + context.l10n.select_squad_select_admin_captain_title, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), content: _selectCaptainContent(context, notifier, state), actionsAlignment: MainAxisAlignment.spaceAround, actions: [ @@ -50,8 +55,11 @@ class SelectAdminAndCaptainDialog extends ConsumerWidget { ); } - Widget _selectCaptainContent(BuildContext context, - SelectSquadViewNotifier notifier, SelectSquadViewState state) { + Widget _selectCaptainContent( + BuildContext context, + SelectSquadViewNotifier notifier, + SelectSquadViewState state, + ) { return SingleChildScrollView( child: Wrap( runSpacing: 16, @@ -65,10 +73,11 @@ class SelectAdminAndCaptainDialog extends ConsumerWidget { } Widget _userProfileCell( - BuildContext context, - SelectSquadViewNotifier notifier, - SelectSquadViewState state, - MatchPlayer member) { + BuildContext context, + SelectSquadViewNotifier notifier, + SelectSquadViewState state, + MatchPlayer member, + ) { return Row( children: [ ImageAvatar( @@ -99,10 +108,8 @@ class SelectAdminAndCaptainDialog extends ConsumerWidget { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - member.player.phone!.substring(1, 3), - member.player.phone! - .substring(member.player.phone!.length - 2)), + member.player.phone + .format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), @@ -122,25 +129,33 @@ class SelectAdminAndCaptainDialog extends ConsumerWidget { spacing: 8, children: [ _captainAdminButton( - context, - context.l10n.select_squad_captain_short_title, - state.captainId == member.player.id, () { - notifier.onCaptainOrAdminSelect( - PlayerMatchRole.captain, member.player.id); - }), + context, + title: context.l10n.select_squad_captain_short_title, + isSelected: state.captainId == member.player.id, + onTap: () => notifier.onCaptainOrAdminSelect( + PlayerMatchRole.captain, + member.player.id, + ), + ), _captainAdminButton( - context, - context.l10n.select_squad_admin_short_title, - state.adminId == member.player.id, () { - notifier.onCaptainOrAdminSelect( - PlayerMatchRole.admin, member.player.id); - }), + context, + title: context.l10n.select_squad_admin_short_title, + isSelected: state.adminId == member.player.id, + onTap: () => notifier.onCaptainOrAdminSelect( + PlayerMatchRole.admin, + member.player.id, + ), + ), ], ); } Widget _captainAdminButton( - BuildContext context, String title, bool isSelected, Function() onTap) { + BuildContext context, { + required String title, + required bool isSelected, + required Function() onTap, + }) { return OnTapScale( onTap: () { if (!isSelected) { diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart new file mode 100644 index 00000000..66a64447 --- /dev/null +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart @@ -0,0 +1,154 @@ +import 'package:data/api/user/user_models.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; +import 'package:khelo/components/image_avatar.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +class UserDetailSheet extends StatelessWidget { + static Future show( + BuildContext context, + UserModel user, + ) { + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: false, + showDragHandle: true, + useRootNavigator: true, + backgroundColor: context.colorScheme.surface, + builder: (context) { + return UserDetailSheet( + user: user, + ); + }, + ); + } + + final UserModel user; + + const UserDetailSheet({super.key, required this.user}); + + @override + Widget build(BuildContext context) { + return Container( + padding: context.mediaQueryPadding + + const EdgeInsets.only(bottom: 24, left: 16, right: 16), + child: Wrap( + alignment: WrapAlignment.center, + children: [ + _nameText(context), + Divider( + height: 32, + color: context.colorScheme.outline, + thickness: 2, + ), + _textDividerView( + context, + title1: user.location.toString(), + title2: user.gender?.getString(context) ?? "", + ), + Divider( + height: 32, + color: context.colorScheme.outline, + thickness: 2, + ), + _textDividerView(context, + title1: user.player_role?.getString(context), + title2: user.batting_style?.getString(context), + title3: user.bowling_style?.getString(context)), + ], + ), + ); + } + + Widget _nameText(BuildContext context) { + return Row( + children: [ + ImageAvatar( + initial: user.nameInitial, + imageUrl: user.profile_img_url, + size: 70, + ), + Expanded( + child: Column( + children: [ + Text( + user.name ?? context.l10n.common_anonymous_title, + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, fontSize: 20), + ), + Text( + user.phone.format(context, StringFormats.obscurePhoneNumber), + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, fontSize: 20), + ), + ], + ), + ), + Text.rich( + textAlign: TextAlign.right, + TextSpan( + text: context.l10n.add_match_joined_on_title, + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textSecondary), + children: [ + TextSpan( + text: + "\n${DateFormat.yMMMd().format(user.created_at ?? DateTime.now())}", + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textSecondary)) + ])), + ], + ); + } + + Widget _textDividerView( + BuildContext context, { + required String? title1, + required String? title2, + String? title3, + }) { + return IntrinsicHeight( + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + if (title1 != null) ...[ + Text( + title1, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), + ], + if (title2 != null) ...[ + VerticalDivider( + thickness: 2, + color: context.colorScheme.outline, + width: 24, + ), + Text( + title2, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), + ], + if (title3 != null) ...[ + VerticalDivider( + thickness: 2, + color: context.colorScheme.outline, + width: 24, + ), + Text( + title3, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), + ], + ], + ), + ); + } +} diff --git a/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart b/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart index ac3d96d3..797cf9af 100644 --- a/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart +++ b/khelo/lib/ui/flow/matches/add_match/select_squad/select_squad_screen.dart @@ -9,7 +9,9 @@ import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/domain/extensions/widget_extension.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/flow/matches/add_match/select_squad/components/select_admin_and_captain_dialog.dart'; +import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart'; import 'package:khelo/ui/flow/matches/add_match/select_squad/select_squad_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -82,8 +84,11 @@ class _SelectSquadScreenState extends ConsumerState { ); } - Widget _playingSquadList(BuildContext context, - SelectSquadViewNotifier notifier, SelectSquadViewState state) { + Widget _playingSquadList( + BuildContext context, + SelectSquadViewNotifier notifier, + SelectSquadViewState state, + ) { return Wrap( children: [ _sectionTitle( @@ -95,15 +100,18 @@ class _SelectSquadScreenState extends ConsumerState { _emptyList(context.l10n.select_squad_empty_squad_text) ] else ...[ for (final member in state.squad) ...[ - _userProfileCell(context, notifier, member, true) + _userProfileCell(context, notifier, member: member, isRemove: true) ], ], ], ); } - Widget _teamMemberList(BuildContext context, SelectSquadViewNotifier notifier, - SelectSquadViewState state) { + Widget _teamMemberList( + BuildContext context, + SelectSquadViewNotifier notifier, + SelectSquadViewState state, + ) { return Wrap( children: [ _sectionTitle(context, context.l10n.select_squad_rest_of_team_title), @@ -112,7 +120,11 @@ class _SelectSquadScreenState extends ConsumerState { ] else ...[ for (final member in getFilteredList(state)) ...[ _userProfileCell( - context, notifier, MatchPlayer(player: member), false) + context, + notifier, + member: MatchPlayer(player: member), + isRemove: false, + ) ], ], ], @@ -167,11 +179,15 @@ class _SelectSquadScreenState extends ConsumerState { ); } - Widget _userProfileCell(BuildContext context, - SelectSquadViewNotifier notifier, MatchPlayer member, bool isRemove) { + Widget _userProfileCell( + BuildContext context, + SelectSquadViewNotifier notifier, { + required MatchPlayer member, + required bool isRemove, + }) { return GestureDetector( onTap: () { - // TODO: show userDetail Sheet + UserDetailSheet.show(context, member.player); }, child: Row( children: [ @@ -203,10 +219,8 @@ class _SelectSquadScreenState extends ConsumerState { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - member.player.phone!.substring(1, 3), - member.player.phone! - .substring(member.player.phone!.length - 2)), + member.player.phone + .format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), @@ -214,13 +228,22 @@ class _SelectSquadScreenState extends ConsumerState { ], ), ), - _trailingButton(context, notifier, member, isRemove) + _trailingButton( + context, + notifier, + member: member, + isRemove: isRemove, + ) ], )); } - Widget _trailingButton(BuildContext context, SelectSquadViewNotifier notifier, - MatchPlayer member, bool isRemove) { + Widget _trailingButton( + BuildContext context, + SelectSquadViewNotifier notifier, { + required MatchPlayer member, + required bool isRemove, + }) { if (isRemove) { return IconButton( onPressed: () => notifier.removeFromSquad(member.player), diff --git a/khelo/lib/ui/flow/matches/match_list_screen.dart b/khelo/lib/ui/flow/matches/match_list_screen.dart index 7a62c7ac..6dc59101 100644 --- a/khelo/lib/ui/flow/matches/match_list_screen.dart +++ b/khelo/lib/ui/flow/matches/match_list_screen.dart @@ -1,10 +1,10 @@ import 'package:data/api/match/match_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:intl/intl.dart'; import 'package:khelo/components/match_status_tag.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/app_route.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -68,9 +68,7 @@ class MatchListScreen extends ConsumerWidget { padding: const EdgeInsets.only(bottom: 40), itemCount: state.matches!.length, separatorBuilder: (context, index) { - return const SizedBox( - height: 16, - ); + return const SizedBox(height: 16); }, itemBuilder: (context, index) { final match = state.matches![index]; @@ -81,7 +79,11 @@ class MatchListScreen extends ConsumerWidget { } else { return Expanded( child: Center( - child: Text(context.l10n.match_list_no_match_yet_title), + child: Text( + context.l10n.match_list_no_match_yet_title, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), ), ); } @@ -99,81 +101,8 @@ class MatchListScreen extends ConsumerWidget { child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - match.match_type.getString(context), - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary), - ), - Text(match.ground, - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary)), - Text( - DateFormat( - 'EEE, MMM dd yyyy ${context.is24HourFormat ? 'HH:mm' : 'hh:mm a'}') - .format(match.start_time), - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary)), - Text( - context.l10n - .match_list_overs_title(match.number_of_over), - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary)), - Divider(color: context.colorScheme.outline), - ], - ), - ), - if (match.match_status != MatchStatus.finish) ...[ - IconButton( - onPressed: () { - if (match.match_status == MatchStatus.yetToStart) { - AppRoute.addMatch(matchId: match.id).push(context); - } else { - AppRoute.scoreBoard(matchId: match.id ?? "INVALID_ID") - .push(context); - } - }, - icon: Icon( - match.match_status == MatchStatus.yetToStart - ? Icons.edit - : Icons.play_arrow_sharp, - size: 30, - )), - ], - ], - ), - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _teamNameView( - context, - team: match.teams.first, - wicket: match.teams.last.wicket ?? 0, - totalOvers: match.number_of_over, - isRunning: match.current_playing_team_id == - match.teams.first.team.id, - ), - _teamNameView( - context, - team: match.teams.last, - wicket: match.teams.first.wicket ?? 0, - totalOvers: match.number_of_over, - isRunning: match.current_playing_team_id == - match.teams.last.team.id, - ), - ], - ), - MatchStatusTag(status: match.match_status) - ], - ), + _matchOtherDetail(context, match), + _teamsAndStatusView(context, match), if (match.match_status == MatchStatus.finish) ...[ _winnerMessageText(context, match) ] @@ -182,6 +111,93 @@ class MatchListScreen extends ConsumerWidget { ); } + Widget _matchOtherDetail(BuildContext context, MatchModel match) { + return Row( + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + match.match_type.getString(context), + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + Text(match.ground, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary)), + Text(match.start_time.format(context, DateFormatType.dateAndTime), + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary)), + Text(context.l10n.match_list_overs_title(match.number_of_over), + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary)), + Divider(color: context.colorScheme.outline), + ], + ), + ), + _matchEditOrResumeActionButton(context, match), + ], + ); + } + + Widget _matchEditOrResumeActionButton( + BuildContext context, + MatchModel match, + ) { + if (match.match_status != MatchStatus.finish) { + return IconButton( + onPressed: () { + if (match.match_status == MatchStatus.yetToStart) { + AppRoute.addMatch(matchId: match.id).push(context); + } else { + AppRoute.scoreBoard(matchId: match.id ?? "INVALID_ID") + .push(context); + } + }, + icon: Icon( + match.match_status == MatchStatus.yetToStart + ? Icons.edit + : Icons.play_arrow_sharp, + size: 30, + )); + } else { + return const SizedBox(); + } + } + + Widget _teamsAndStatusView(BuildContext context, MatchModel match) { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _teamNameView( + context, + team: match.teams.first, + wicket: match.teams.last.wicket ?? 0, + totalOvers: match.number_of_over, + isRunning: + match.current_playing_team_id == match.teams.first.team.id && + match.match_status == MatchStatus.running, + ), + _teamNameView( + context, + team: match.teams.last, + wicket: match.teams.first.wicket ?? 0, + totalOvers: match.number_of_over, + isRunning: + match.current_playing_team_id == match.teams.last.team.id && + match.match_status == MatchStatus.running, + ), + ], + ), + MatchStatusTag(status: match.match_status) + ], + ); + } + Widget _teamNameView( BuildContext context, { required MatchTeamModel team, @@ -233,8 +249,12 @@ class MatchListScreen extends ConsumerWidget { final runDifference = firstTeam.run! - secondTeam.run!; - return _messageText(context, teamName, runDifference, - context.l10n.score_board_runs_dot_title); + return _messageText( + context, + teamName, + difference: runDifference, + trailingText: context.l10n.score_board_runs_dot_title, + ); } else { // second batting team won final teamName = secondTeam.team.name; @@ -242,13 +262,21 @@ class MatchListScreen extends ConsumerWidget { final wicketDifference = secondTeam.squad.length - (firstTeam.wicket ?? 0); - return _messageText(context, teamName, wicketDifference, - context.l10n.score_board_wickets_dot_title); + return _messageText( + context, + teamName, + difference: wicketDifference, + trailingText: context.l10n.score_board_wickets_dot_title, + ); } } - Widget _messageText(BuildContext context, String? teamName, int difference, - String trailingText) { + Widget _messageText( + BuildContext context, + String? teamName, { + required int difference, + required String trailingText, + }) { return Text.rich(TextSpan( text: "$teamName", style: diff --git a/khelo/lib/ui/flow/matches/match_list_view_model.dart b/khelo/lib/ui/flow/matches/match_list_view_model.dart index 3563501b..f0729505 100644 --- a/khelo/lib/ui/flow/matches/match_list_view_model.dart +++ b/khelo/lib/ui/flow/matches/match_list_view_model.dart @@ -22,11 +22,12 @@ class MatchListViewNotifier extends StateNotifier { Future loadMatches() async { state = state.copyWith(loading: true); try { - final matches = await _matchService.getMatches(); + final matches = await _matchService.getCurrentUserRelatedMatches(); state = state.copyWith(matches: matches, loading: false); } catch (e, stack) { state = state.copyWith(loading: false, error: e); - debugPrint("MatchListViewNotifier: error while load matches -> $e ,\nstack: $stack"); + debugPrint( + "MatchListViewNotifier: error while load matches -> $e ,\nstack: $stack"); } } } @@ -35,7 +36,7 @@ class MatchListViewNotifier extends StateNotifier { class MatchListViewState with _$MatchListViewState { const factory MatchListViewState({ Object? error, - @Default(false) bool loading, List? matches, + @Default(false) bool loading, }) = _MatchListViewState; } diff --git a/khelo/lib/ui/flow/matches/match_list_view_model.freezed.dart b/khelo/lib/ui/flow/matches/match_list_view_model.freezed.dart index 807732ad..47c4746e 100644 --- a/khelo/lib/ui/flow/matches/match_list_view_model.freezed.dart +++ b/khelo/lib/ui/flow/matches/match_list_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'match_list_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$MatchListViewState { diff --git a/khelo/lib/ui/flow/profile/profile_screen.dart b/khelo/lib/ui/flow/profile/profile_screen.dart index c123b745..735353a1 100644 --- a/khelo/lib/ui/flow/profile/profile_screen.dart +++ b/khelo/lib/ui/flow/profile/profile_screen.dart @@ -34,11 +34,12 @@ class ProfileScreen extends ConsumerWidget { title: context.l10n.tab_profile_title, actions: [ TextButton( - onPressed: () { - _showSignOutAlert(context, () { - notifier.onSignOutTap(); - }); - }, + onPressed: () => _showSignOutAlert( + context, + onSignOut: () { + notifier.onSignOutTap(); + }, + ), child: Text( context.l10n.common_sign_out_title, style: AppTextStyle.button @@ -92,7 +93,9 @@ class ProfileScreen extends ConsumerWidget { child: state.currentUser?.profile_img_url == null ? Text(state.currentUser?.nameInitial ?? '?', style: AppTextStyle.header1.copyWith( - color: context.colorScheme.secondary, fontSize: 40)) + color: context.colorScheme.secondary, + fontSize: 40, + )) : null, ), const SizedBox( @@ -150,7 +153,10 @@ class ProfileScreen extends ConsumerWidget { } } - void _showSignOutAlert(BuildContext context, Function() onSignOut) { + void _showSignOutAlert( + BuildContext context, { + required Function() onSignOut, + }) { showAdaptiveDialog( context: context, builder: (context) { @@ -160,9 +166,7 @@ class ProfileScreen extends ConsumerWidget { context.l10n.common_sign_out_title.toLowerCase())), actions: [ TextButton( - onPressed: () { - context.pop(); - }, + onPressed: () => context.pop(), child: Text( context.l10n.common_cancel_title, style: TextStyle(color: context.colorScheme.textSecondary), diff --git a/khelo/lib/ui/flow/profile/profile_view_model.dart b/khelo/lib/ui/flow/profile/profile_view_model.dart index 97242200..64fed029 100644 --- a/khelo/lib/ui/flow/profile/profile_view_model.dart +++ b/khelo/lib/ui/flow/profile/profile_view_model.dart @@ -13,6 +13,7 @@ final profileStateProvider = ref.read(authServiceProvider), ref.read(currentUserPod), ); + ref.listen(currentUserPod, (_, next) => notifier._updateUser(next)); return notifier; }); @@ -38,5 +39,7 @@ class ProfileViewNotifier extends StateNotifier { @freezed class ProfileState with _$ProfileState { - const factory ProfileState({UserModel? currentUser}) = _ProfileState; + const factory ProfileState({ + UserModel? currentUser, + }) = _ProfileState; } diff --git a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_screen.dart b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_screen.dart index a049054f..4028dd26 100644 --- a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_screen.dart +++ b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_screen.dart @@ -126,9 +126,7 @@ class _AddTossDetailScreenState extends ConsumerState { image: getTossDecisionImage(decision), title: decision.getString(context), isSelected: state.tossWinnerDecision == decision, - onTap: () { - notifier.onOptionSelect(decision); - }, + onTap: () => notifier.onOptionSelect(decision), )) .toList(), ), @@ -216,7 +214,7 @@ class _AddTossDetailScreenState extends ConsumerState { }) { return Expanded( child: OnTapScale( - onTap: () => onTap(), + onTap: onTap, child: Column( children: [ Container( @@ -251,8 +249,11 @@ class _AddTossDetailScreenState extends ConsumerState { ); } - Widget _stickyButton(BuildContext context, AddTossDetailViewNotifier notifier, - AddTossDetailState state) { + Widget _stickyButton( + BuildContext context, + AddTossDetailViewNotifier notifier, + AddTossDetailState state, + ) { return BottomStickyOverlay( child: PrimaryButton( context.l10n.common_next_title, diff --git a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.freezed.dart b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.freezed.dart index 17442b59..e26c1a1f 100644 --- a/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.freezed.dart +++ b/khelo/lib/ui/flow/score_board/add_toss_detail/add_toss_detail_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'add_toss_detail_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$AddTossDetailState { diff --git a/khelo/lib/ui/flow/score_board/components/inning_complete_dialog.dart b/khelo/lib/ui/flow/score_board/components/inning_complete_dialog.dart index 2e43522d..ef8c11cd 100644 --- a/khelo/lib/ui/flow/score_board/components/inning_complete_dialog.dart +++ b/khelo/lib/ui/flow/score_board/components/inning_complete_dialog.dart @@ -37,16 +37,12 @@ class InningCompleteDialog extends ConsumerWidget { PrimaryButton( expanded: false, context.l10n.score_board_undo_last_ball_title, - onPressed: () { - context.pop(false); - }, + onPressed: () => context.pop(false), ), PrimaryButton( context.l10n.score_board_start_next_inning_title, expanded: false, - onPressed: () { - context.pop(true); - }, + onPressed: () => context.pop(true), ), ], ); @@ -55,6 +51,7 @@ class InningCompleteDialog extends ConsumerWidget { Widget _inningContent(BuildContext context, ScoreBoardViewState state) { final teamName = _getCurrentTeamName(context, state); final extras = _getExtras(state); + return Wrap( direction: Axis.vertical, children: [ @@ -65,24 +62,36 @@ class InningCompleteDialog extends ConsumerWidget { ), Row( children: [ - _subTitleText(context, "${context.l10n.score_board_runs_title}:", - state.totalRuns.toString()), + _subTitleText( + context, + title: "${context.l10n.score_board_runs_title}:", + subTitle: state.totalRuns.toString(), + ), const SizedBox( width: 16, ), - _subTitleText(context, "${context.l10n.score_board_wickets_text}:", - state.wicketCount.toString()) + _subTitleText( + context, + title: "${context.l10n.score_board_wickets_text}:", + subTitle: state.wicketCount.toString(), + ) ], ), Row( children: [ - _subTitleText(context, "${context.l10n.score_board_extras_title}:", - extras.toString()), + _subTitleText( + context, + title: "${context.l10n.score_board_extras_title}:", + subTitle: extras.toString(), + ), const SizedBox( width: 16, ), - _subTitleText(context, "${context.l10n.score_board_overs_title}:", - _getOverCount(state)) + _subTitleText( + context, + title: "${context.l10n.score_board_overs_title}:", + subTitle: _getOverCount(state), + ) ], ), ], @@ -93,7 +102,11 @@ class InningCompleteDialog extends ConsumerWidget { return "${state.overCount - 1}.${state.ballCount}"; } - Widget _subTitleText(BuildContext context, String title, String subTitle) { + Widget _subTitleText( + BuildContext context, { + required String title, + required String subTitle, + }) { return Text.rich(TextSpan( text: title, style: AppTextStyle.subtitle2 diff --git a/khelo/lib/ui/flow/score_board/components/is_boundary_dialog.dart b/khelo/lib/ui/flow/score_board/components/is_boundary_dialog.dart index ed7de8ef..f0f34aed 100644 --- a/khelo/lib/ui/flow/score_board/components/is_boundary_dialog.dart +++ b/khelo/lib/ui/flow/score_board/components/is_boundary_dialog.dart @@ -35,33 +35,35 @@ class IsBoundaryDialog extends StatelessWidget { actionsAlignment: MainAxisAlignment.spaceAround, actionsOverflowButtonSpacing: 8, actions: [ - OnTapScale( - onTap: () { - context.pop(false); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), - child: Text( - context.l10n.common_no_title, - style: AppTextStyle.button - .copyWith(color: context.colorScheme.textPrimary), - ), - ), + _actionButton( + context, + title: context.l10n.common_no_title, + onTap: () => context.pop(false), ), - OnTapScale( - onTap: () { - context.pop(true); - }, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), - child: Text( - context.l10n.common_yes_title, - style: AppTextStyle.button - .copyWith(color: context.colorScheme.textPrimary), - ), - ), + _actionButton( + context, + title: context.l10n.common_yes_title, + onTap: () => context.pop(true), ), ], ); } + + Widget _actionButton( + BuildContext context, { + required String title, + required Function() onTap, + }) { + return OnTapScale( + onTap: onTap, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8), + child: Text( + title, + style: AppTextStyle.button + .copyWith(color: context.colorScheme.textPrimary), + ), + ), + ); + } } diff --git a/khelo/lib/ui/flow/score_board/components/match_complete_dialog.dart b/khelo/lib/ui/flow/score_board/components/match_complete_dialog.dart index eaf2974b..8cba6c85 100644 --- a/khelo/lib/ui/flow/score_board/components/match_complete_dialog.dart +++ b/khelo/lib/ui/flow/score_board/components/match_complete_dialog.dart @@ -37,16 +37,12 @@ class MatchCompleteDialog extends ConsumerWidget { PrimaryButton( expanded: false, context.l10n.score_board_undo_last_ball_title, - onPressed: () { - context.pop(false); - }, + onPressed: () => context.pop(false), ), PrimaryButton( context.l10n.score_board_end_match_title, expanded: false, - onPressed: () { - context.pop(true); - }, + onPressed: () => context.pop(true), ), ], ); diff --git a/khelo/lib/ui/flow/score_board/components/over_complete_dialog.dart b/khelo/lib/ui/flow/score_board/components/over_complete_dialog.dart index 71819c54..a99b905c 100644 --- a/khelo/lib/ui/flow/score_board/components/over_complete_dialog.dart +++ b/khelo/lib/ui/flow/score_board/components/over_complete_dialog.dart @@ -39,16 +39,12 @@ class OverCompleteDialog extends ConsumerWidget { PrimaryButton( expanded: false, context.l10n.score_board_undo_last_ball_title, - onPressed: () { - context.pop(false); - }, + onPressed: () => context.pop(false), ), PrimaryButton( context.l10n.score_board_start_next_over_title, expanded: false, - onPressed: () { - context.pop(true); - }, + onPressed: () => context.pop(true), ), ], ); @@ -63,18 +59,27 @@ class OverCompleteDialog extends ConsumerWidget { _overSummaryText(context, state), Row( children: [ - _subTitleText(context, "${context.l10n.score_board_runs_title}:", - run.toString()), + _subTitleText( + context, + title: "${context.l10n.score_board_runs_title}:", + subTitle: run.toString(), + ), const SizedBox( width: 8, ), - _subTitleText(context, "${context.l10n.score_board_wickets_text}:", - wicket.toString()), + _subTitleText( + context, + title: "${context.l10n.score_board_wickets_text}:", + subTitle: wicket.toString(), + ), const SizedBox( width: 8, ), - _subTitleText(context, "${context.l10n.score_board_extras_title}:", - extra.toString()), + _subTitleText( + context, + title: "${context.l10n.score_board_extras_title}:", + subTitle: extra.toString(), + ), ], ), const Divider(), @@ -111,7 +116,11 @@ class OverCompleteDialog extends ConsumerWidget { ])); } - Widget _subTitleText(BuildContext context, String title, String subTitle) { + Widget _subTitleText( + BuildContext context, { + required String title, + required String subTitle, + }) { return Text.rich(TextSpan( text: title, style: AppTextStyle.subtitle2 @@ -148,7 +157,10 @@ class OverCompleteDialog extends ConsumerWidget { } Widget _batsManIndividualScore( - BuildContext context, ScoreBoardViewState state, MatchPlayer batsMan) { + BuildContext context, + ScoreBoardViewState state, + MatchPlayer batsMan, + ) { final (run, ball) = _getBatsManTotalRuns(state, batsMan.player.id); return Text.rich(TextSpan( text: batsMan.player.name, diff --git a/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart b/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart index 02d8a197..5da709fc 100644 --- a/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/select_player_sheet.dart @@ -139,7 +139,6 @@ class _SelectPlayerSheetState extends ConsumerState { context: context, user: bowler?.player, onTap: () async { - // call itSelf for bowler selection final player = await SelectPlayerSheet.show< List<({String teamId, List players})>>( context, @@ -191,7 +190,7 @@ class _SelectPlayerSheetState extends ConsumerState { }) { return Expanded( child: OnTapScale( - onTap: () => onTap(), + onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( @@ -236,7 +235,9 @@ class _SelectPlayerSheetState extends ConsumerState { } Widget _specificPlayerSelectionView( - BuildContext context, ScoreBoardViewState state) { + BuildContext context, + ScoreBoardViewState state, + ) { final list = getFilteredList(state); return Stack( children: [ @@ -258,12 +259,12 @@ class _SelectPlayerSheetState extends ConsumerState { childAspectRatio: 0.7), itemCount: list.length, itemBuilder: (context, index) { - return _userCell1( + return _userCellWithTag( context: context, state: state, user: list[index].player, tag: list[index].status == PlayerStatus.injured - ? "Injured" + ? context.l10n.score_board_injured_tag_title : null, isSelected: widget.type == PlayerSelectionType.batsMan ? batsMan1?.player.id == list[index].player.id @@ -316,7 +317,7 @@ class _SelectPlayerSheetState extends ConsumerState { status != PlayerStatus.suspended; } - Widget _userCell1({ + Widget _userCellWithTag({ required BuildContext context, required ScoreBoardViewState state, UserModel? user, @@ -324,9 +325,9 @@ class _SelectPlayerSheetState extends ConsumerState { required bool isSelected, required Function() onTap, }) { - final overCount = _getOverCount(state, user?.id ?? "INVALID_ID"); + final overCount = _getOverCount(state, user?.id ?? "INVALID ID"); return OnTapScale( - onTap: () => onTap(), + onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( @@ -435,4 +436,9 @@ class _SelectPlayerSheetState extends ConsumerState { } } -enum PlayerSelectionType { bowler, batsMan, all, batsManAndBowler } +enum PlayerSelectionType { + bowler, + batsMan, + all, + batsManAndBowler, +} diff --git a/khelo/lib/ui/flow/score_board/components/select_wicket_taker_sheet.dart b/khelo/lib/ui/flow/score_board/components/select_wicket_taker_sheet.dart index 98a08ab5..6171b558 100644 --- a/khelo/lib/ui/flow/score_board/components/select_wicket_taker_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/select_wicket_taker_sheet.dart @@ -108,7 +108,7 @@ class _SelectWicketTakerSheetState childAspectRatio: 0.7), itemCount: list.length, itemBuilder: (context, index) { - return _userCell1( + return _userCell( context: context, user: list[index].player, isSelected: selectedId == list[index].player.id, @@ -140,14 +140,14 @@ class _SelectWicketTakerSheetState return teamPlayers ?? []; } - Widget _userCell1({ + Widget _userCell({ required BuildContext context, UserModel? user, required bool isSelected, required Function() onTap, }) { return OnTapScale( - onTap: () => onTap(), + onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( @@ -180,17 +180,13 @@ class _SelectWicketTakerSheetState ); } - Widget _stickyButton( - BuildContext context, - ) { + Widget _stickyButton(BuildContext context) { return Align( alignment: Alignment.bottomCenter, child: PrimaryButton( context.l10n.score_board_select_title, enabled: selectedId != null, - onPressed: () { - context.pop(selectedId); - }, + onPressed: () => context.pop(selectedId), ), ); } diff --git a/khelo/lib/ui/flow/score_board/components/striker_selection_dialog.dart b/khelo/lib/ui/flow/score_board/components/striker_selection_dialog.dart index f977f97a..097c2888 100644 --- a/khelo/lib/ui/flow/score_board/components/striker_selection_dialog.dart +++ b/khelo/lib/ui/flow/score_board/components/striker_selection_dialog.dart @@ -26,8 +26,10 @@ class StrikerSelectionDialog extends ConsumerStatefulWidget { final bool isForStrikerSelection; - const StrikerSelectionDialog( - {super.key, required this.isForStrikerSelection}); + const StrikerSelectionDialog({ + super.key, + required this.isForStrikerSelection, + }); @override ConsumerState createState() => _StrikerSelectionDialogState(); @@ -93,7 +95,7 @@ class _StrikerSelectionDialogState required Function() onTap, }) { return OnTapScale( - onTap: () => onTap(), + onTap: onTap, child: Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( diff --git a/khelo/lib/ui/flow/score_board/score_board_screen.dart b/khelo/lib/ui/flow/score_board/score_board_screen.dart index b0c9cd32..a0eece55 100644 --- a/khelo/lib/ui/flow/score_board/score_board_screen.dart +++ b/khelo/lib/ui/flow/score_board/score_board_screen.dart @@ -301,7 +301,9 @@ class _ScoreBoardScreenState extends ConsumerState { } Widget _moreOptionButton( - BuildContext context, ScoreBoardViewNotifier notifier) { + BuildContext context, + ScoreBoardViewNotifier notifier, + ) { return PopupMenuButton( color: context.colorScheme.containerNormalOnSurface, onSelected: notifier.onMatchOptionSelect, @@ -586,9 +588,7 @@ class _ScoreBoardScreenState extends ConsumerState { children: [ for (final ball in _getFilteredCurrentOverBall(state)) ...[ _ballView(context, ball), - const SizedBox( - width: 8, - ) + const SizedBox(width: 8) ] ], ), @@ -677,7 +677,7 @@ class _ScoreBoardScreenState extends ConsumerState { type == WicketType.caughtBehind) && context.mounted) { wicketTakerId = await SelectWicketTakerSheet.show(context); - } // who caught the ball + } int? extra; if (type == WicketType.runOut && context.mounted) { @@ -789,9 +789,14 @@ class _ScoreBoardScreenState extends ConsumerState { } Future _showAddExtraDialog( - BuildContext context, ExtrasType? extra) async { - final extraData = await AddExtraDialog.show<(int, bool, bool)>(context, - extrasType: extra, isFiveSeven: extra == null); + BuildContext context, + ExtrasType? extra, + ) async { + final extraData = await AddExtraDialog.show<(int, bool, bool)>( + context, + extrasType: extra, + isFiveSeven: extra == null, + ); if (context.mounted && extraData != null) { int runs = extraData.$1; bool isBoundary = extraData.$2; @@ -812,9 +817,7 @@ class _ScoreBoardScreenState extends ConsumerState { extra: runs, ); } else { - notifier.addBall( - run: runs, - ); + notifier.addBall(run: runs); } } } diff --git a/khelo/lib/ui/flow/score_board/score_board_view_model.dart b/khelo/lib/ui/flow/score_board/score_board_view_model.dart index 3edc1cd8..7725cc5c 100644 --- a/khelo/lib/ui/flow/score_board/score_board_view_model.dart +++ b/khelo/lib/ui/flow/score_board/score_board_view_model.dart @@ -29,8 +29,10 @@ class ScoreBoardViewNotifier extends StateNotifier { String? matchId; ScoreBoardViewNotifier( - this._matchService, this._inningService, this._ballScoreService) - : super(const ScoreBoardViewState()); + this._matchService, + this._inningService, + this._ballScoreService, + ) : super(const ScoreBoardViewState()); void setData(String matchId) { this.matchId = matchId; @@ -415,7 +417,6 @@ class ScoreBoardViewNotifier extends StateNotifier { if (state.otherInning?.innings_status == InningStatus.finish && (state.totalRuns > (state.otherInning?.total_runs ?? 0))) { - // targeted-run-achieved state = state.copyWith(showMatchCompleteDialog: DateTime.now()); } else if (((playing?.length == 1) && (yetToPlay?.isEmpty ?? true) && @@ -478,7 +479,6 @@ class ScoreBoardViewNotifier extends StateNotifier { wicket: state.wicketCount, runs: state.otherInning?.total_runs); - // update match state final teams = state.match?.teams.toList(); teams?.map((e) { if (e.team.id == state.currentInning?.team_id) { @@ -529,7 +529,6 @@ class ScoreBoardViewNotifier extends StateNotifier { final outPlayer = battingTeam?.firstWhere( (element) => element.player.id == lastBall?.player_out_id); - // every new player would be appended at last final newBatsMan = state.batsMans?.elementAtOrNull(1); if (newBatsMan != null && outPlayer != null) { final updateList = [ diff --git a/khelo/lib/ui/flow/score_board/score_board_view_model.freezed.dart b/khelo/lib/ui/flow/score_board/score_board_view_model.freezed.dart index 875a81ea..de382c20 100644 --- a/khelo/lib/ui/flow/score_board/score_board_view_model.freezed.dart +++ b/khelo/lib/ui/flow/score_board/score_board_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'score_board_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$ScoreBoardViewState { 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 3688a70f..7809e42b 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 @@ -56,8 +56,10 @@ class EditProfileScreen extends ConsumerWidget { actions: [ if (!isToCreateAccount) ...[ IconButton( - onPressed: () => - _showDeleteAlert(context, () => notifier.onDeleteTap()), + onPressed: () => _showDeleteAlert( + context, + onDelete: () => notifier.onDeleteTap(), + ), icon: const Icon(Icons.delete_outline)) ] ], @@ -231,8 +233,11 @@ class EditProfileScreen extends ConsumerWidget { ); } - Widget _profileImageView(BuildContext context, - EditProfileViewNotifier notifier, EditProfileState state) { + Widget _profileImageView( + BuildContext context, + EditProfileViewNotifier notifier, + EditProfileState state, + ) { return Center( child: SizedBox( height: profileViewHeight, @@ -295,8 +300,13 @@ class EditProfileScreen extends ConsumerWidget { ); } - Widget _textInputField(BuildContext context, EditProfileViewNotifier notifier, - EditProfileState state, String label, TextEditingController controller) { + Widget _textInputField( + BuildContext context, + EditProfileViewNotifier notifier, + EditProfileState state, + String label, + TextEditingController controller, + ) { return TextField( controller: controller, onChanged: (value) => notifier.onValueChange(), @@ -330,8 +340,11 @@ class EditProfileScreen extends ConsumerWidget { ); } - Future _selectDate(BuildContext context, - EditProfileViewNotifier notifier, EditProfileState state) async { + Future _selectDate( + BuildContext context, + EditProfileViewNotifier notifier, + EditProfileState state, + ) async { final DateTime? picked = await showDatePicker( context: context, helpText: context.l10n.edit_profile_select_birth_date_placeholder, @@ -343,8 +356,11 @@ class EditProfileScreen extends ConsumerWidget { } } - Widget _genderOptionView(BuildContext context, - EditProfileViewNotifier notifier, EditProfileState state) { + Widget _genderOptionView( + BuildContext context, + EditProfileViewNotifier notifier, + EditProfileState state, + ) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -355,8 +371,12 @@ class EditProfileScreen extends ConsumerWidget { ); } - Widget _radioBtnCell(BuildContext context, EditProfileViewNotifier notifier, - EditProfileState state, UserGender gender) { + Widget _radioBtnCell( + BuildContext context, + EditProfileViewNotifier notifier, + EditProfileState state, + UserGender gender, + ) { return OnTapScale( onTap: () => notifier.onGenderSelect(gender: gender), child: Wrap( @@ -382,7 +402,10 @@ class EditProfileScreen extends ConsumerWidget { ); } - void _showDeleteAlert(BuildContext context, Function() onDelete) { + void _showDeleteAlert( + BuildContext context, { + required Function() onDelete, + }) { showAdaptiveDialog( context: context, builder: (context) { 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 0c80e77a..5fcdd376 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 @@ -242,6 +242,7 @@ class EditProfileState with _$EditProfileState { required TextEditingController nameController, required TextEditingController emailController, required TextEditingController locationController, + UserModel? currentUser, @Default(null) String? imageUrl, @Default(null) UserGender? gender, @Default(null) BattingStyle? battingStyle, @@ -251,6 +252,5 @@ class EditProfileState with _$EditProfileState { @Default(false) bool isImageUploading, @Default(false) bool isSaved, @Default(false) bool isSaveInProgress, - UserModel? currentUser, }) = _EditProfileState; } diff --git a/khelo/lib/ui/flow/sign_in/phone_verification/components/resend_code_view.dart b/khelo/lib/ui/flow/sign_in/phone_verification/components/resend_code_view.dart index f0754d72..ee1010e0 100644 --- a/khelo/lib/ui/flow/sign_in/phone_verification/components/resend_code_view.dart +++ b/khelo/lib/ui/flow/sign_in/phone_verification/components/resend_code_view.dart @@ -10,7 +10,10 @@ import '../phone_verification_view_model.dart'; class PhoneVerificationResendCodeView extends ConsumerWidget { final String phoneNumber; - const PhoneVerificationResendCodeView({super.key, required this.phoneNumber}); + const PhoneVerificationResendCodeView({ + super.key, + required this.phoneNumber, + }); @override Widget build(BuildContext context, WidgetRef ref) { diff --git a/khelo/lib/ui/flow/sign_in/phone_verification/phone_verification_view_model.dart b/khelo/lib/ui/flow/sign_in/phone_verification/phone_verification_view_model.dart index 907be06e..36119501 100644 --- a/khelo/lib/ui/flow/sign_in/phone_verification/phone_verification_view_model.dart +++ b/khelo/lib/ui/flow/sign_in/phone_verification/phone_verification_view_model.dart @@ -115,13 +115,13 @@ class PhoneVerificationViewNotifier @freezed class PhoneVerificationState with _$PhoneVerificationState { const factory PhoneVerificationState({ + Object? error, + String? verificationId, @Default(false) bool verifying, @Default(false) bool enableVerify, @Default(false) bool isVerificationComplete, @Default(false) bool showErrorVerificationCodeText, - String? verificationId, @Default('') String otp, @Default(Duration(seconds: 30)) Duration activeResendDuration, - Object? error, }) = _PhoneVerificationState; } diff --git a/khelo/lib/ui/flow/sign_in/sign_in_with_phone/components/sign_in_with_phone_country_picker.dart b/khelo/lib/ui/flow/sign_in/sign_in_with_phone/components/sign_in_with_phone_country_picker.dart index 87d3ffa6..2b6541a7 100644 --- a/khelo/lib/ui/flow/sign_in/sign_in_with_phone/components/sign_in_with_phone_country_picker.dart +++ b/khelo/lib/ui/flow/sign_in/sign_in_with_phone/components/sign_in_with_phone_country_picker.dart @@ -13,7 +13,6 @@ class SignInWithPhoneCountryPicker extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final notifier = ref.watch(signInWithPhoneStateProvider.notifier); final countryCode = ref.watch( signInWithPhoneStateProvider.select((value) => value.code), @@ -42,9 +41,7 @@ class SignInWithPhoneCountryPicker extends ConsumerWidget { filter: filter, ), codeBuilder: (code) => GestureDetector( - onTap: () { - context.pop(code); - }, + onTap: () => context.pop(code), child: DefaultCountryCodeListItemView( code: code, dialCodeStyle: AppTextStyle.body1 diff --git a/khelo/lib/ui/flow/sign_in/sign_in_with_phone/sign_in_with_phone_view_model.dart b/khelo/lib/ui/flow/sign_in/sign_in_with_phone/sign_in_with_phone_view_model.dart index eff33fbc..1be24c2a 100644 --- a/khelo/lib/ui/flow/sign_in/sign_in_with_phone/sign_in_with_phone_view_model.dart +++ b/khelo/lib/ui/flow/sign_in/sign_in_with_phone/sign_in_with_phone_view_model.dart @@ -84,11 +84,11 @@ class SignInWithPhoneViewNotifier extends StateNotifier { class SignInWithPhoneState with _$SignInWithPhoneState { const factory SignInWithPhoneState({ required CountryCode code, + Object? error, + String? verificationId, @Default(false) bool verifying, @Default(false) bool signInSuccess, - String? verificationId, @Default(false) bool enableBtn, - Object? error, @Default('') String phone, }) = _SignInWithPhoneState; } diff --git a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_screen.dart b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_screen.dart index 5ae9a6df..7c477ee6 100644 --- a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_screen.dart +++ b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_screen.dart @@ -53,17 +53,18 @@ class _MatchDetailStatScreenState extends ConsumerState { return _teamDetailView(context, state, state.match!.teams[index]); }, separatorBuilder: (context, index) { - return const SizedBox( - height: 16, - ); + return const SizedBox(height: 16); }, itemCount: state.match?.teams.length ?? 0); } return const SizedBox(); } - Widget _teamDetailView(BuildContext context, MatchDetailStatViewState state, - MatchTeamModel team) { + Widget _teamDetailView( + BuildContext context, + MatchDetailStatViewState state, + MatchTeamModel team, + ) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( @@ -121,8 +122,11 @@ class _MatchDetailStatScreenState extends ConsumerState { ); } - Widget _teamScoreView(BuildContext context, MatchDetailStatViewState state, - MatchTeamModel team) { + Widget _teamScoreView( + BuildContext context, + MatchDetailStatViewState state, + MatchTeamModel team, + ) { return Column( children: [ _runScoredText(context, team.run ?? 0, team.over ?? 0), @@ -150,8 +154,12 @@ class _MatchDetailStatScreenState extends ConsumerState { return Row( children: [ Expanded( - child: _boundaryCountCell( - context, context.l10n.match_stat_wicket_taken_title, wicket)) + child: _boundaryCountCell( + context, + context.l10n.common_wicket_taken_title, + wicket, + ), + ) ], ); } @@ -228,8 +236,11 @@ class _MatchDetailStatScreenState extends ConsumerState { ); } - Widget _squadExpansionView(BuildContext context, - MatchDetailStatViewState state, MatchTeamModel team) { + Widget _squadExpansionView( + BuildContext context, + MatchDetailStatViewState state, + MatchTeamModel team, + ) { return Material( color: Colors.transparent, child: Card( @@ -365,7 +376,10 @@ class _MatchDetailStatScreenState extends ConsumerState { } Widget _extraAwarded( - BuildContext context, MatchDetailStatViewState state, String teamId) { + BuildContext context, + MatchDetailStatViewState state, + String teamId, + ) { final extra = _calculateExtras(state, teamId); return Row( children: [ @@ -385,4 +399,4 @@ class _MatchDetailStatScreenState extends ConsumerState { 0, (sum, element) => (sum ?? 0) + (element.extras_awarded ?? 0)) ?? 0; } -} \ No newline at end of file +} diff --git a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.dart b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.dart index adb663e8..90842725 100644 --- a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.dart +++ b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.dart @@ -25,8 +25,10 @@ class MatchDetailStatViewNotifier final BallScoreService _ballScoreService; MatchDetailStatViewNotifier( - this._matchService, this._inningService, this._ballScoreService) - : super(const MatchDetailStatViewState()); + this._matchService, + this._inningService, + this._ballScoreService, + ) : super(const MatchDetailStatViewState()); void setData(String matchId) { state = state.copyWith(matchId: matchId); @@ -79,12 +81,12 @@ class MatchDetailStatViewNotifier class MatchDetailStatViewState with _$MatchDetailStatViewState { const factory MatchDetailStatViewState({ Object? error, - @Default(false) bool loading, String? matchId, MatchModel? match, InningModel? firstInning, InningModel? secondInning, List? firstScore, List? secondScore, + @Default(false) bool loading, }) = _MatchDetailStatViewState; } diff --git a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.freezed.dart b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.freezed.dart index 44768348..1135edfc 100644 --- a/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.freezed.dart +++ b/khelo/lib/ui/flow/stats/user_match/match_detail_stat/match_detail_stat_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'match_detail_stat_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$MatchDetailStatViewState { diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart index d72c005e..2b858027 100644 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart +++ b/khelo/lib/ui/flow/stats/user_match/user_match_list_screen.dart @@ -2,10 +2,10 @@ 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:intl/intl.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/stats/user_match/user_match_list_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; @@ -78,10 +78,7 @@ class UserMatchListScreen extends ConsumerWidget { Text(match.ground, style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textPrimary)), - Text( - DateFormat( - 'EEE, MMM dd yyyy ${context.is24HourFormat ? 'HH:mm' : 'hh:mm a'}') - .format(match.start_time), + Text(match.start_time.format(context, DateFormatType.dateAndTime), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textPrimary)), Divider(color: context.colorScheme.outline), @@ -142,8 +139,12 @@ class UserMatchListScreen extends ConsumerWidget { } } - Widget _messageText(BuildContext context, String? teamName, int difference, - String trailingText) { + Widget _messageText( + BuildContext context, + String? teamName, + int difference, + String trailingText, + ) { return Text.rich(TextSpan( text: "$teamName", style: diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart index db3283d1..17bfa802 100644 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart +++ b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.dart @@ -24,7 +24,7 @@ class UserMatchListViewNotifier extends StateNotifier { Future loadUserMatches() async { state = state.copyWith(loading: true); try { - final matches = await _matchService.getCurrentUserMatches(); + final matches = await _matchService.getCurrentUserPlayedMatches(); state = state.copyWith(matches: matches, loading: false); } catch (e) { state = state.copyWith(error: e, loading: false); diff --git a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart index 822fc4aa..d3957176 100644 --- a/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart +++ b/khelo/lib/ui/flow/stats/user_match/user_match_list_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'user_match_list_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$UserMatchListState { diff --git a/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart b/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart index cbccab6e..6b5c362a 100644 --- a/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart +++ b/khelo/lib/ui/flow/stats/user_stat/user_stat_screen.dart @@ -285,7 +285,7 @@ class UserStatScreen extends ConsumerWidget { children: [ _sectionTitle( context, context.l10n.my_stat_stats_bowling_statics_title), - _runScoredView(context, context.l10n.my_stat_stats_wicket_taken_title, + _runScoredView(context, context.l10n.common_wicket_taken_title, wickets.toString()), const SizedBox(height: 16), _averageAndStrikeRateView(context, state, true), @@ -354,14 +354,18 @@ class UserStatScreen extends ConsumerWidget { return Row( children: [ Expanded( - child: _averageCellView(context, - context.l10n.my_stat_stats_run_out_title, runOut.toString())), + child: _averageCellView( + context, + context.l10n.my_stat_stats_run_out_title, + runOut.toString(), + )), const SizedBox(width: 16), Expanded( child: _averageCellView( - context, - context.l10n.my_stat_stats_stumping_title, - stumping.toString())), + context, + context.l10n.my_stat_stats_stumping_title, + stumping.toString(), + )), ], ); } 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 9570f928..75d10b14 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 @@ -11,6 +11,7 @@ import 'package:khelo/components/image_picker_sheet.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/domain/extensions/widget_extension.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/team/add_team/add_team_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; @@ -90,9 +91,7 @@ class _AddTeamScreenState extends ConsumerState { actionButton( context, onPressed: () { - _showDeleteAlert(context, () { - notifier.onTeamDelete(); - }); + _showDeleteAlert(context, onDelete: notifier.onTeamDelete); }, icon: Icon( Icons.delete_outline, @@ -245,7 +244,10 @@ class _AddTeamScreenState extends ConsumerState { } Widget _profileImageView( - BuildContext context, AddTeamViewNotifier notifier, AddTeamState state) { + BuildContext context, + AddTeamViewNotifier notifier, + AddTeamState state, + ) { return Center( child: SizedBox( height: profileViewHeight, @@ -308,8 +310,12 @@ class _AddTeamScreenState extends ConsumerState { ); } - Widget _userProfileCell(BuildContext context, AddTeamViewNotifier notifier, - AddTeamState state, UserModel user) { + Widget _userProfileCell( + BuildContext context, + AddTeamViewNotifier notifier, + AddTeamState state, + UserModel user, + ) { return Row( children: [ ImageAvatar( @@ -340,9 +346,7 @@ class _AddTeamScreenState extends ConsumerState { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - user.phone!.substring(1, 3), - user.phone!.substring(user.phone!.length - 2)), + user.phone.format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), @@ -367,14 +371,15 @@ class _AddTeamScreenState extends ConsumerState { !state.isImageUploading && !state.checkingForAvailability, progress: state.isAddInProgress, - onPressed: () { - notifier.onAddBtnTap(); - }, + onPressed: () => notifier.onAddBtnTap(), ), ); } - void _showDeleteAlert(BuildContext context, Function() onDelete) { + void _showDeleteAlert( + BuildContext context, { + required Function() onDelete, + }) { showAdaptiveDialog( context: context, builder: (context) { diff --git a/khelo/lib/ui/flow/team/add_team/add_team_view_model.dart b/khelo/lib/ui/flow/team/add_team/add_team_view_model.dart index 415b303d..72da6878 100644 --- a/khelo/lib/ui/flow/team/add_team/add_team_view_model.dart +++ b/khelo/lib/ui/flow/team/add_team/add_team_view_model.dart @@ -252,6 +252,11 @@ class AddTeamState with _$AddTeamState { const factory AddTeamState({ required TextEditingController nameController, required TextEditingController locationController, + String? imageUrl, + bool? isNameAvailable, + TeamModel? team, + TeamModel? editTeam, + UserModel? currentUser, @Default(false) bool isImageUploading, @Default(true) bool isAddMeCheckBoxEnable, @Default(false) bool checkingForAvailability, @@ -259,10 +264,5 @@ class AddTeamState with _$AddTeamState { @Default(false) bool isAddInProgress, @Default(false) bool isPop, @Default([]) List teamMembers, - String? imageUrl, - bool? isNameAvailable, - TeamModel? team, - TeamModel? editTeam, - UserModel? currentUser, }) = _AddTeamState; } diff --git a/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart b/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart index 66e931f4..2869cfad 100644 --- a/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart +++ b/khelo/lib/ui/flow/team/add_team_member/add_team_member_screen.dart @@ -7,6 +7,8 @@ import 'package:khelo/components/app_page.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; +import 'package:khelo/ui/flow/matches/add_match/select_squad/components/user_detail_sheet.dart'; import 'package:khelo/ui/flow/team/add_team_member/add_team_member_view_model.dart'; import 'package:khelo/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart'; import 'package:style/animations/on_tap_scale.dart'; @@ -133,14 +135,13 @@ class AddTeamMemberScreen extends ConsumerWidget { } Widget _userProfileCell( - BuildContext context, - AddTeamMemberViewNotifier notifier, - AddTeamMemberState state, - UserModel user) { + BuildContext context, + AddTeamMemberViewNotifier notifier, + AddTeamMemberState state, + UserModel user, + ) { return GestureDetector( - onTap: () { - // TODO: show userDetail Sheet - }, + onTap: () => UserDetailSheet.show(context, user), child: Row( children: [ ImageAvatar( @@ -171,9 +172,8 @@ class AddTeamMemberScreen extends ConsumerWidget { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - user.phone!.substring(1, 3), - user.phone!.substring(user.phone!.length - 2)), + user.phone + .format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), diff --git a/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart b/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart index 29fe932c..6e24243f 100644 --- a/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart +++ b/khelo/lib/ui/flow/team/add_team_member/add_team_member_view_model.dart @@ -78,9 +78,9 @@ class AddTeamMemberViewNotifier extends StateNotifier { class AddTeamMemberState with _$AddTeamMemberState { const factory AddTeamMemberState({ required TextEditingController searchController, + Object? error, @Default([]) List searchedUsers, @Default([]) List selectedUsers, - Object? error, @Default(false) bool isAdded, @Default(false) bool isAddInProgress, }) = _AddTeamMemberState; diff --git a/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart b/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart index e08a1e16..27cae57a 100644 --- a/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart +++ b/khelo/lib/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart @@ -22,7 +22,10 @@ class VerifyAddTeamMemberDialog extends StatefulWidget { final String phoneNumber; - const VerifyAddTeamMemberDialog({super.key, required this.phoneNumber}); + const VerifyAddTeamMemberDialog({ + super.key, + required this.phoneNumber, + }); @override State createState() => @@ -41,37 +44,41 @@ class _VerifyAddTeamMemberDialogState extends State { style: AppTextStyle.header1 .copyWith(color: context.colorScheme.textPrimary, fontSize: 26), ), - content: Wrap( - children: [ - Text( - context.l10n.add_team_member_verify_placeholder_text, - style: AppTextStyle.header4 - .copyWith(color: context.colorScheme.textPrimary, fontSize: 20), - ), - TextField( - maxLength: 5, - decoration: InputDecoration( - counterStyle: AppTextStyle.body1 - .copyWith(color: context.colorScheme.textDisabled)), - textAlign: TextAlign.center, - style: AppTextStyle.subtitle1 - .copyWith(color: context.colorScheme.textPrimary, fontSize: 34), - inputFormatters: [ - FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), - ], - onChanged: (value) { - setState(() { - verificationNumber = value; - }); - }, - ), - ], + content: IntrinsicHeight( + child: Column( + children: [ + Text( + context.l10n.add_team_member_verify_placeholder_text, + style: AppTextStyle.header4.copyWith( + color: context.colorScheme.textPrimary, + fontSize: 20, + ), + ), + TextField( + maxLength: 5, + decoration: InputDecoration( + counterStyle: AppTextStyle.body1 + .copyWith(color: context.colorScheme.textDisabled)), + textAlign: TextAlign.center, + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, + fontSize: 34, + ), + inputFormatters: [ + FilteringTextInputFormatter.allow(RegExp(r'[0-9]')), + ], + onChanged: (value) { + setState(() { + verificationNumber = value; + }); + }, + ), + ], + ), ), actions: [ TextButton( - onPressed: () { - context.pop(); - }, + onPressed: () => context.pop(), child: Text( context.l10n.common_cancel_title, style: AppTextStyle.button.copyWith( @@ -79,13 +86,7 @@ class _VerifyAddTeamMemberDialogState extends State { )), TextButton( onPressed: verificationNumber.trim().length == 5 - ? () { - if (widget.phoneNumber == verificationNumber) { - context.pop(true); - } else { - context.pop(); - } - } + ? () => context.pop(widget.phoneNumber == verificationNumber) : null, child: Text( context.l10n.add_team_member_verify_title, diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart index cd6690be..bc440421 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_match_content.dart @@ -1,10 +1,10 @@ import 'package:data/api/match/match_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:intl/intl.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/components/match_status_tag.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/flow/team/detail/team_detail_view_model.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; @@ -40,7 +40,10 @@ class TeamDetailMatchContent extends ConsumerWidget { const SizedBox( height: 16, ), - ] + ], + const SizedBox( + height: 34, + ), ], ); } @@ -69,14 +72,12 @@ class TeamDetailMatchContent extends ConsumerWidget { .copyWith(color: context.colorScheme.textPrimary), ), Text( - "Overs: ${match.number_of_over.toString()}", + "${context.l10n.team_detail_overs_title} ${match.number_of_over.toString()}", style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), Text( - DateFormat( - 'EEE, MMM dd yyyy ${context.is24HourFormat ? 'HH:mm' : 'hh:mm a'}') - .format(match.start_time), + match.start_time.format(context, DateFormatType.dateAndTime), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart index 2e3d27e4..74b4f3b8 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_member_content.dart @@ -39,7 +39,10 @@ class TeamDetailMemberContent extends ConsumerWidget { const SizedBox( height: 16, ), - ] + ], + const SizedBox( + height: 34, + ), ], ); } diff --git a/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart b/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart index 65b96c82..7b7bc926 100644 --- a/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart +++ b/khelo/lib/ui/flow/team/detail/components/team_detail_stat_content.dart @@ -57,7 +57,8 @@ class TeamDetailStatContent extends ConsumerWidget { const SizedBox(height: 16), _highestAndLowestRunCount(context, state), const SizedBox(height: 16), - _runRateCount(context, state) + _runRateCount(context, state), + const SizedBox(height: 50), ], ); } diff --git a/khelo/lib/ui/flow/team/detail/team_detail_screen.dart b/khelo/lib/ui/flow/team/detail/team_detail_screen.dart index b8c44c7d..45a47c40 100644 --- a/khelo/lib/ui/flow/team/detail/team_detail_screen.dart +++ b/khelo/lib/ui/flow/team/detail/team_detail_screen.dart @@ -11,6 +11,7 @@ import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_style.dart'; +import 'package:style/page_views/expandable_page_view.dart'; import 'components/team_detail_match_content.dart'; import 'components/team_detail_stat_content.dart'; @@ -119,24 +120,22 @@ class _TeamDetailScreenState extends ConsumerState { } Widget _content(BuildContext context) { - return SizedBox( - height: 800, - child: Column( - children: [ - _tabView(context), - Expanded( - child: PageView( - controller: _controller, - children: _tabs, - onPageChanged: (index) { - setState(() { - notifier.onTabChange(index); - }); - }, - ), - ), - ], - ), + return Column( + children: [ + _tabView(context), + ExpandablePageView( + itemCount: _tabs.length, + controller: _controller, + itemBuilder: (context, index) { + return _tabs[index]; + }, + onPageChanged: (index) { + setState(() { + notifier.onTabChange(index); + }); + }, + ), + ], ); } @@ -195,4 +194,10 @@ class _TeamDetailScreenState extends ConsumerState { ), ); } + + @override + void dispose() { + _controller.dispose(); + super.dispose(); + } } diff --git a/khelo/lib/ui/flow/team/detail/team_detail_view_model.freezed.dart b/khelo/lib/ui/flow/team/detail/team_detail_view_model.freezed.dart index f1057319..adff1d00 100644 --- a/khelo/lib/ui/flow/team/detail/team_detail_view_model.freezed.dart +++ b/khelo/lib/ui/flow/team/detail/team_detail_view_model.freezed.dart @@ -12,7 +12,7 @@ part of 'team_detail_view_model.dart'; T _$identity(T value) => value; final _privateConstructorUsedError = UnsupportedError( - 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#custom-getters-and-methods'); + 'It seems like you constructed your class using `MyClass._()`. This constructor is only meant to be used by freezed and you are not supposed to need it nor use it.\nPlease check the documentation here for more information: https://github.com/rrousselGit/freezed#adding-getters-and-methods-to-our-models'); /// @nodoc mixin _$TeamDetailState { diff --git a/khelo/lib/ui/flow/team/search_team/components/team_member_dialog.dart b/khelo/lib/ui/flow/team/search_team/components/team_member_dialog.dart index 31880ec2..7de30f8e 100644 --- a/khelo/lib/ui/flow/team/search_team/components/team_member_dialog.dart +++ b/khelo/lib/ui/flow/team/search_team/components/team_member_dialog.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/formatter/string_formatter.dart'; import 'package:khelo/ui/flow/team/add_team_member/components/verify_add_team_member_dialog.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -30,14 +31,21 @@ class TeamMemberDialog extends StatelessWidget { final TeamModel team; final bool isForVerification; - const TeamMemberDialog( - {super.key, required this.team, this.isForVerification = false}); + const TeamMemberDialog({ + super.key, + required this.team, + this.isForVerification = false, + }); @override Widget build(BuildContext context) { return AlertDialog( backgroundColor: context.colorScheme.containerLowOnSurface, - title: Text(team.name), + title: Text( + team.name, + style: AppTextStyle.subtitle1 + .copyWith(color: context.colorScheme.textPrimary), + ), content: SingleChildScrollView( child: Wrap( runSpacing: 16, @@ -82,9 +90,7 @@ class TeamMemberDialog extends StatelessWidget { height: 2, ), Text( - context.l10n.common_obscure_phone_number_text( - user.phone!.substring(1, 3), - user.phone!.substring(user.phone!.length - 2)), + user.phone.format(context, StringFormats.obscurePhoneNumber), style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textSecondary), ), diff --git a/khelo/lib/ui/flow/team/search_team/search_team_screen.dart b/khelo/lib/ui/flow/team/search_team/search_team_screen.dart index 00cc049e..438f182d 100644 --- a/khelo/lib/ui/flow/team/search_team/search_team_screen.dart +++ b/khelo/lib/ui/flow/team/search_team/search_team_screen.dart @@ -151,8 +151,6 @@ class _SearchTeamScreenState extends ConsumerState { ), _teamProfileCell(context, notifier, state, team) ], - - // show team member of current user if (state.userTeams.isNotEmpty) ...[ _sectionTitle(context, context.l10n.search_team_your_teams_title), for (final team in state.userTeams) ...[ @@ -166,14 +164,18 @@ class _SearchTeamScreenState extends ConsumerState { ); } - Widget _teamProfileCell(BuildContext context, SearchTeamViewNotifier notifier, - SearchTeamState state, TeamModel team) { + Widget _teamProfileCell( + BuildContext context, + SearchTeamViewNotifier notifier, + SearchTeamState state, + TeamModel team, + ) { return OnTapScale( onTap: () { notifier.onTeamCellTap(team); }, onLongTap: () { - // TODO: show members of team in dialog (play haptic feedback). + // TODO: play haptic feedback. TeamMemberDialog.show(context, team: team); }, child: Row( @@ -203,7 +205,8 @@ class _SearchTeamScreenState extends ConsumerState { .copyWith(color: context.colorScheme.textSecondary), children: [ TextSpan( - text: " - ${team.players?.length ?? 0} Members") + text: + " - ${context.l10n.search_team_member_title(team.players?.length ?? 0)}") ])), ], ), diff --git a/khelo/lib/ui/flow/team/search_team/search_team_view_model.dart b/khelo/lib/ui/flow/team/search_team/search_team_view_model.dart index dbde0edf..4c6d4f3d 100644 --- a/khelo/lib/ui/flow/team/search_team/search_team_view_model.dart +++ b/khelo/lib/ui/flow/team/search_team/search_team_view_model.dart @@ -38,7 +38,7 @@ class SearchTeamViewNotifier extends StateNotifier { state = state.copyWith(userTeams: filteredResult, loading: false); } catch (e) { state = state.copyWith(loading: false); - debugPrint("SearchTeamViewNotifier: error while loading team list"); + debugPrint("SearchTeamViewNotifier: error while loading team list -> $e"); } } @@ -76,10 +76,10 @@ class SearchTeamViewNotifier extends StateNotifier { class SearchTeamState with _$SearchTeamState { const factory SearchTeamState({ required TextEditingController searchController, + Object? error, + TeamModel? selectedTeam, @Default([]) List searchResults, @Default([]) List userTeams, - TeamModel? selectedTeam, @Default(false) bool loading, - Object? error, }) = _SearchTeamState; } diff --git a/khelo/lib/ui/flow/team/team_list_screen.dart b/khelo/lib/ui/flow/team/team_list_screen.dart index ec126fd6..29cfe438 100644 --- a/khelo/lib/ui/flow/team/team_list_screen.dart +++ b/khelo/lib/ui/flow/team/team_list_screen.dart @@ -17,11 +17,13 @@ class TeamListScreen extends ConsumerWidget { const TeamListScreen({super.key}); void _observeShowFilterOptionSheet( - BuildContext context, WidgetRef ref, TeamListViewNotifier notifier) { + BuildContext context, + WidgetRef ref, + ) { ref.listen( teamListViewStateProvider .select((value) => value.showFilterOptionSheet), (previous, next) { - showFilterOptionSheet(context, notifier); + SelectFilterOptionSheet.show(context); }); } @@ -30,12 +32,15 @@ class TeamListScreen extends ConsumerWidget { final notifier = ref.watch(teamListViewStateProvider.notifier); final state = ref.watch(teamListViewStateProvider); - _observeShowFilterOptionSheet(context, ref, notifier); + _observeShowFilterOptionSheet(context, ref); return _teamList(context, notifier, state); } - Widget _teamList(BuildContext context, TeamListViewNotifier notifier, - TeamListViewState state) { + Widget _teamList( + BuildContext context, + TeamListViewNotifier notifier, + TeamListViewState state, + ) { if (state.loading) { return const Center(child: AppProgressIndicator()); } @@ -77,7 +82,10 @@ class TeamListScreen extends ConsumerWidget { } Widget _teamListCell( - BuildContext context, TeamListViewNotifier notifier, TeamModel team) { + BuildContext context, + TeamListViewNotifier notifier, + TeamModel team, + ) { return OnTapScale( onTap: () { AppRoute.teamDetail(teamId: team.id ?? "INVALID ID").push(context); @@ -171,11 +179,4 @@ class TeamListScreen extends ConsumerWidget { ), ); } - - void showFilterOptionSheet( - BuildContext context, - TeamListViewNotifier notifier, - ) { - SelectFilterOptionSheet.show(context); - } } diff --git a/style/lib/page_views/expandable_page_view.dart b/style/lib/page_views/expandable_page_view.dart new file mode 100644 index 00000000..4a9aefc7 --- /dev/null +++ b/style/lib/page_views/expandable_page_view.dart @@ -0,0 +1,116 @@ +import 'package:flutter/material.dart'; + +class ExpandablePageView extends StatefulWidget { + final int itemCount; + final Widget Function(BuildContext, int) itemBuilder; + final PageController? controller; + final ValueChanged? onPageChanged; + final bool reverse; + + const ExpandablePageView({ + required this.itemCount, + required this.itemBuilder, + this.controller, + this.onPageChanged, + this.reverse = false, + super.key, + }); + + @override + State createState() => _ExpandablePageViewState(); +} + +class _ExpandablePageViewState extends State { + PageController? _pageController; + List _heights = []; + int _currentPage = 0; + + double get _currentHeight => _heights[_currentPage]; + + @override + void initState() { + super.initState(); + _heights = List.filled(widget.itemCount, 0, growable: true); + _pageController = widget.controller ?? PageController(); + _pageController?.addListener(_updatePage); + } + + @override + void dispose() { + _pageController?.removeListener(_updatePage); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return TweenAnimationBuilder( + curve: Curves.easeInOutCubic, + tween: Tween(begin: _heights.first, end: _currentHeight), + duration: const Duration(milliseconds: 100), + builder: (context, value, child) => SizedBox(height: value, child: child), + child: PageView.builder( + controller: _pageController, + itemCount: widget.itemCount, + itemBuilder: _itemBuilder, + onPageChanged: widget.onPageChanged, + reverse: widget.reverse, + ), + ); + } + + Widget _itemBuilder(BuildContext context, int index) { + final item = widget.itemBuilder(context, index); + return OverflowBox( + minHeight: 0, + maxHeight: double.infinity, + alignment: Alignment.topCenter, + child: SizeReportingWidget( + onSizeChange: (size) => setState(() => _heights[index] = size.height), + child: item, + ), + ); + } + + void _updatePage() { + final newPage = _pageController?.page?.round(); + if (_currentPage != newPage) { + setState(() { + _currentPage = newPage ?? _currentPage; + }); + } + } +} + +class SizeReportingWidget extends StatefulWidget { + final Widget child; + final ValueChanged onSizeChange; + + const SizeReportingWidget({ + required this.child, + required this.onSizeChange, + super.key, + }); + + @override + State createState() => _SizeReportingWidgetState(); +} + +class _SizeReportingWidgetState extends State { + Size? _oldSize; + + @override + Widget build(BuildContext context) { + WidgetsBinding.instance.addPostFrameCallback((_) => _notifySize()); + return widget.child; + } + + void _notifySize() { + if (mounted) { + final size = context.size; + if (_oldSize != size) { + _oldSize = size; + if (size != null) widget.onSizeChange(size); + } + } + } +}