From d8aedf4a068805dabd5a56ec32ccdb587606f52b Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Mon, 23 Sep 2024 16:33:50 +0530 Subject: [PATCH 01/33] Add tournament --- data/lib/api/tournament/tournament_model.dart | 80 +++ .../tournament/tournament_model.freezed.dart | 607 ++++++++++++++++++ .../api/tournament/tournament_model.g.dart | 66 ++ .../tournament/tournament_service.dart | 34 + .../utils/constant/firestore_constant.dart | 1 + khelo/assets/images/ic_tournaments.svg | 4 + khelo/assets/locales/app_en.arb | 7 +- .../lib/components/profile_image_avatar.dart | 15 +- khelo/lib/gen/assets.gen.dart | 4 + khelo/lib/ui/app_route.dart | 11 + .../ui/flow/my_game/my_game_tab_screen.dart | 36 +- .../tournament/add/add_tournament_screen.dart | 70 ++ .../add/add_tournament_view_model.dart | 46 ++ .../add_tournament_view_model.freezed.dart | 250 ++++++++ .../tournament/tournament_list_screen.dart | 26 + style/lib/button/action_button.dart | 3 + 16 files changed, 1247 insertions(+), 13 deletions(-) create mode 100644 data/lib/api/tournament/tournament_model.dart create mode 100644 data/lib/api/tournament/tournament_model.freezed.dart create mode 100644 data/lib/api/tournament/tournament_model.g.dart create mode 100644 data/lib/service/tournament/tournament_service.dart create mode 100644 khelo/assets/images/ic_tournaments.svg create mode 100644 khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart create mode 100644 khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart create mode 100644 khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart create mode 100644 khelo/lib/ui/flow/tournament/tournament_list_screen.dart diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart new file mode 100644 index 00000000..950e99f1 --- /dev/null +++ b/data/lib/api/tournament/tournament_model.dart @@ -0,0 +1,80 @@ +// ignore_for_file: non_constant_identifier_names + +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:json_serializable/type_helper.dart'; + +import '../match/match_model.dart'; +import '../team/team_model.dart'; + +part 'tournament_model.freezed.dart'; + +part 'tournament_model.g.dart'; + +@freezed +class TournamentModel with _$TournamentModel { + @JsonSerializable(anyMap: true, explicitToJson: true) + const factory TournamentModel({ + required String id, + required String name, + String? profile_img_url, + String? banner_img_url, + @Default(TournamentType.other) type, + @Default([]) List members, + @JsonConverterHelper() required DateTime start_date, + @JsonConverterHelper() required DateTime end_date, + @Default([]) List team_ids, + @Default([]) List match_ids, + @JsonKey(includeFromJson: false, includeToJson: false) + @Default([]) + List teams, + @JsonKey(includeFromJson: false, includeToJson: false) + @Default([]) + List matches, + }) = _TournamentModel; + + factory TournamentModel.fromJson(Map json) => + _$TournamentModelFromJson(json); + + factory TournamentModel.fromFireStore( + DocumentSnapshot> snapshot, + SnapshotOptions? options, + ) => + TournamentModel.fromJson(snapshot.data()!); +} + +@freezed +class TournamentMember with _$TournamentMember { + const factory TournamentMember({ + required String id, + @Default(TournamentMemberRole.admin) TournamentMemberRole role, + }) = _TournamentMember; + + factory TournamentMember.fromJson(Map json) => + _$TournamentMemberFromJson(json); +} + +enum TournamentType { + knockOut(1), + miniRobin(2), + boxLeague(3), + doubleOut(4), + superOver(5), + bestOf(6), + gully(7), + mixed(8), + other(9); + + final int value; + + const TournamentType(this.value); +} + +enum TournamentMemberRole { + organizer, + admin; + + bool get isOrganizer => this == TournamentMemberRole.organizer; + + bool get isAdmin => this == TournamentMemberRole.admin; +} diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart new file mode 100644 index 00000000..c9c0ab83 --- /dev/null +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -0,0 +1,607 @@ +// 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 'tournament_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'); + +TournamentModel _$TournamentModelFromJson(Map json) { + return _TournamentModel.fromJson(json); +} + +/// @nodoc +mixin _$TournamentModel { + String get id => throw _privateConstructorUsedError; + String get name => throw _privateConstructorUsedError; + String? get profile_img_url => throw _privateConstructorUsedError; + String? get banner_img_url => throw _privateConstructorUsedError; + dynamic get type => throw _privateConstructorUsedError; + List get members => throw _privateConstructorUsedError; + @JsonConverterHelper() + DateTime get start_date => throw _privateConstructorUsedError; + @JsonConverterHelper() + DateTime get end_date => throw _privateConstructorUsedError; + List get team_ids => throw _privateConstructorUsedError; + List get match_ids => throw _privateConstructorUsedError; + @JsonKey(includeFromJson: false, includeToJson: false) + List get teams => throw _privateConstructorUsedError; + @JsonKey(includeFromJson: false, includeToJson: false) + List get matches => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $TournamentModelCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TournamentModelCopyWith<$Res> { + factory $TournamentModelCopyWith( + TournamentModel value, $Res Function(TournamentModel) then) = + _$TournamentModelCopyWithImpl<$Res, TournamentModel>; + @useResult + $Res call( + {String id, + String name, + String? profile_img_url, + String? banner_img_url, + dynamic type, + List members, + @JsonConverterHelper() DateTime start_date, + @JsonConverterHelper() DateTime end_date, + List team_ids, + List match_ids, + @JsonKey(includeFromJson: false, includeToJson: false) + List teams, + @JsonKey(includeFromJson: false, includeToJson: false) + List matches}); +} + +/// @nodoc +class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> + implements $TournamentModelCopyWith<$Res> { + _$TournamentModelCopyWithImpl(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? id = null, + Object? name = null, + Object? profile_img_url = freezed, + Object? banner_img_url = freezed, + Object? type = freezed, + Object? members = null, + Object? start_date = null, + Object? end_date = null, + Object? team_ids = null, + Object? match_ids = null, + Object? teams = null, + Object? matches = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + profile_img_url: freezed == profile_img_url + ? _value.profile_img_url + : profile_img_url // ignore: cast_nullable_to_non_nullable + as String?, + banner_img_url: freezed == banner_img_url + ? _value.banner_img_url + : banner_img_url // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as dynamic, + members: null == members + ? _value.members + : members // ignore: cast_nullable_to_non_nullable + as List, + start_date: null == start_date + ? _value.start_date + : start_date // ignore: cast_nullable_to_non_nullable + as DateTime, + end_date: null == end_date + ? _value.end_date + : end_date // ignore: cast_nullable_to_non_nullable + as DateTime, + team_ids: null == team_ids + ? _value.team_ids + : team_ids // ignore: cast_nullable_to_non_nullable + as List, + match_ids: null == match_ids + ? _value.match_ids + : match_ids // ignore: cast_nullable_to_non_nullable + as List, + teams: null == teams + ? _value.teams + : teams // ignore: cast_nullable_to_non_nullable + as List, + matches: null == matches + ? _value.matches + : matches // ignore: cast_nullable_to_non_nullable + as List, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TournamentModelImplCopyWith<$Res> + implements $TournamentModelCopyWith<$Res> { + factory _$$TournamentModelImplCopyWith(_$TournamentModelImpl value, + $Res Function(_$TournamentModelImpl) then) = + __$$TournamentModelImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {String id, + String name, + String? profile_img_url, + String? banner_img_url, + dynamic type, + List members, + @JsonConverterHelper() DateTime start_date, + @JsonConverterHelper() DateTime end_date, + List team_ids, + List match_ids, + @JsonKey(includeFromJson: false, includeToJson: false) + List teams, + @JsonKey(includeFromJson: false, includeToJson: false) + List matches}); +} + +/// @nodoc +class __$$TournamentModelImplCopyWithImpl<$Res> + extends _$TournamentModelCopyWithImpl<$Res, _$TournamentModelImpl> + implements _$$TournamentModelImplCopyWith<$Res> { + __$$TournamentModelImplCopyWithImpl( + _$TournamentModelImpl _value, $Res Function(_$TournamentModelImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? name = null, + Object? profile_img_url = freezed, + Object? banner_img_url = freezed, + Object? type = freezed, + Object? members = null, + Object? start_date = null, + Object? end_date = null, + Object? team_ids = null, + Object? match_ids = null, + Object? teams = null, + Object? matches = null, + }) { + return _then(_$TournamentModelImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + name: null == name + ? _value.name + : name // ignore: cast_nullable_to_non_nullable + as String, + profile_img_url: freezed == profile_img_url + ? _value.profile_img_url + : profile_img_url // ignore: cast_nullable_to_non_nullable + as String?, + banner_img_url: freezed == banner_img_url + ? _value.banner_img_url + : banner_img_url // ignore: cast_nullable_to_non_nullable + as String?, + type: freezed == type ? _value.type! : type, + members: null == members + ? _value._members + : members // ignore: cast_nullable_to_non_nullable + as List, + start_date: null == start_date + ? _value.start_date + : start_date // ignore: cast_nullable_to_non_nullable + as DateTime, + end_date: null == end_date + ? _value.end_date + : end_date // ignore: cast_nullable_to_non_nullable + as DateTime, + team_ids: null == team_ids + ? _value._team_ids + : team_ids // ignore: cast_nullable_to_non_nullable + as List, + match_ids: null == match_ids + ? _value._match_ids + : match_ids // ignore: cast_nullable_to_non_nullable + as List, + teams: null == teams + ? _value._teams + : teams // ignore: cast_nullable_to_non_nullable + as List, + matches: null == matches + ? _value._matches + : matches // ignore: cast_nullable_to_non_nullable + as List, + )); + } +} + +/// @nodoc + +@JsonSerializable(anyMap: true, explicitToJson: true) +class _$TournamentModelImpl implements _TournamentModel { + const _$TournamentModelImpl( + {required this.id, + required this.name, + this.profile_img_url, + this.banner_img_url, + this.type = TournamentType.other, + final List members = const [], + @JsonConverterHelper() required this.start_date, + @JsonConverterHelper() required this.end_date, + final List team_ids = const [], + final List match_ids = const [], + @JsonKey(includeFromJson: false, includeToJson: false) + final List teams = const [], + @JsonKey(includeFromJson: false, includeToJson: false) + final List matches = const []}) + : _members = members, + _team_ids = team_ids, + _match_ids = match_ids, + _teams = teams, + _matches = matches; + + factory _$TournamentModelImpl.fromJson(Map json) => + _$$TournamentModelImplFromJson(json); + + @override + final String id; + @override + final String name; + @override + final String? profile_img_url; + @override + final String? banner_img_url; + @override + @JsonKey() + final dynamic type; + final List _members; + @override + @JsonKey() + List get members { + if (_members is EqualUnmodifiableListView) return _members; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_members); + } + + @override + @JsonConverterHelper() + final DateTime start_date; + @override + @JsonConverterHelper() + final DateTime end_date; + final List _team_ids; + @override + @JsonKey() + List get team_ids { + if (_team_ids is EqualUnmodifiableListView) return _team_ids; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_team_ids); + } + + final List _match_ids; + @override + @JsonKey() + List get match_ids { + if (_match_ids is EqualUnmodifiableListView) return _match_ids; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_match_ids); + } + + final List _teams; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get teams { + if (_teams is EqualUnmodifiableListView) return _teams; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_teams); + } + + final List _matches; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get matches { + if (_matches is EqualUnmodifiableListView) return _matches; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_matches); + } + + @override + String toString() { + return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, banner_img_url: $banner_img_url, type: $type, members: $members, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TournamentModelImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.name, name) || other.name == name) && + (identical(other.profile_img_url, profile_img_url) || + other.profile_img_url == profile_img_url) && + (identical(other.banner_img_url, banner_img_url) || + other.banner_img_url == banner_img_url) && + const DeepCollectionEquality().equals(other.type, type) && + const DeepCollectionEquality().equals(other._members, _members) && + (identical(other.start_date, start_date) || + other.start_date == start_date) && + (identical(other.end_date, end_date) || + other.end_date == end_date) && + const DeepCollectionEquality().equals(other._team_ids, _team_ids) && + const DeepCollectionEquality() + .equals(other._match_ids, _match_ids) && + const DeepCollectionEquality().equals(other._teams, _teams) && + const DeepCollectionEquality().equals(other._matches, _matches)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash( + runtimeType, + id, + name, + profile_img_url, + banner_img_url, + const DeepCollectionEquality().hash(type), + const DeepCollectionEquality().hash(_members), + start_date, + end_date, + const DeepCollectionEquality().hash(_team_ids), + const DeepCollectionEquality().hash(_match_ids), + const DeepCollectionEquality().hash(_teams), + const DeepCollectionEquality().hash(_matches)); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TournamentModelImplCopyWith<_$TournamentModelImpl> get copyWith => + __$$TournamentModelImplCopyWithImpl<_$TournamentModelImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$TournamentModelImplToJson( + this, + ); + } +} + +abstract class _TournamentModel implements TournamentModel { + const factory _TournamentModel( + {required final String id, + required final String name, + final String? profile_img_url, + final String? banner_img_url, + final dynamic type, + final List members, + @JsonConverterHelper() required final DateTime start_date, + @JsonConverterHelper() required final DateTime end_date, + final List team_ids, + final List match_ids, + @JsonKey(includeFromJson: false, includeToJson: false) + final List teams, + @JsonKey(includeFromJson: false, includeToJson: false) + final List matches}) = _$TournamentModelImpl; + + factory _TournamentModel.fromJson(Map json) = + _$TournamentModelImpl.fromJson; + + @override + String get id; + @override + String get name; + @override + String? get profile_img_url; + @override + String? get banner_img_url; + @override + dynamic get type; + @override + List get members; + @override + @JsonConverterHelper() + DateTime get start_date; + @override + @JsonConverterHelper() + DateTime get end_date; + @override + List get team_ids; + @override + List get match_ids; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get teams; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get matches; + @override + @JsonKey(ignore: true) + _$$TournamentModelImplCopyWith<_$TournamentModelImpl> get copyWith => + throw _privateConstructorUsedError; +} + +TournamentMember _$TournamentMemberFromJson(Map json) { + return _TournamentMember.fromJson(json); +} + +/// @nodoc +mixin _$TournamentMember { + String get id => throw _privateConstructorUsedError; + TournamentMemberRole get role => throw _privateConstructorUsedError; + + Map toJson() => throw _privateConstructorUsedError; + @JsonKey(ignore: true) + $TournamentMemberCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TournamentMemberCopyWith<$Res> { + factory $TournamentMemberCopyWith( + TournamentMember value, $Res Function(TournamentMember) then) = + _$TournamentMemberCopyWithImpl<$Res, TournamentMember>; + @useResult + $Res call({String id, TournamentMemberRole role}); +} + +/// @nodoc +class _$TournamentMemberCopyWithImpl<$Res, $Val extends TournamentMember> + implements $TournamentMemberCopyWith<$Res> { + _$TournamentMemberCopyWithImpl(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? id = null, + Object? role = null, + }) { + return _then(_value.copyWith( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as TournamentMemberRole, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TournamentMemberImplCopyWith<$Res> + implements $TournamentMemberCopyWith<$Res> { + factory _$$TournamentMemberImplCopyWith(_$TournamentMemberImpl value, + $Res Function(_$TournamentMemberImpl) then) = + __$$TournamentMemberImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String id, TournamentMemberRole role}); +} + +/// @nodoc +class __$$TournamentMemberImplCopyWithImpl<$Res> + extends _$TournamentMemberCopyWithImpl<$Res, _$TournamentMemberImpl> + implements _$$TournamentMemberImplCopyWith<$Res> { + __$$TournamentMemberImplCopyWithImpl(_$TournamentMemberImpl _value, + $Res Function(_$TournamentMemberImpl) _then) + : super(_value, _then); + + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? id = null, + Object? role = null, + }) { + return _then(_$TournamentMemberImpl( + id: null == id + ? _value.id + : id // ignore: cast_nullable_to_non_nullable + as String, + role: null == role + ? _value.role + : role // ignore: cast_nullable_to_non_nullable + as TournamentMemberRole, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$TournamentMemberImpl implements _TournamentMember { + const _$TournamentMemberImpl( + {required this.id, this.role = TournamentMemberRole.admin}); + + factory _$TournamentMemberImpl.fromJson(Map json) => + _$$TournamentMemberImplFromJson(json); + + @override + final String id; + @override + @JsonKey() + final TournamentMemberRole role; + + @override + String toString() { + return 'TournamentMember(id: $id, role: $role)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TournamentMemberImpl && + (identical(other.id, id) || other.id == id) && + (identical(other.role, role) || other.role == role)); + } + + @JsonKey(ignore: true) + @override + int get hashCode => Object.hash(runtimeType, id, role); + + @JsonKey(ignore: true) + @override + @pragma('vm:prefer-inline') + _$$TournamentMemberImplCopyWith<_$TournamentMemberImpl> get copyWith => + __$$TournamentMemberImplCopyWithImpl<_$TournamentMemberImpl>( + this, _$identity); + + @override + Map toJson() { + return _$$TournamentMemberImplToJson( + this, + ); + } +} + +abstract class _TournamentMember implements TournamentMember { + const factory _TournamentMember( + {required final String id, + final TournamentMemberRole role}) = _$TournamentMemberImpl; + + factory _TournamentMember.fromJson(Map json) = + _$TournamentMemberImpl.fromJson; + + @override + String get id; + @override + TournamentMemberRole get role; + @override + @JsonKey(ignore: true) + _$$TournamentMemberImplCopyWith<_$TournamentMemberImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart new file mode 100644 index 00000000..a0e932ad --- /dev/null +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -0,0 +1,66 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'tournament_model.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +_$TournamentModelImpl _$$TournamentModelImplFromJson(Map json) => + _$TournamentModelImpl( + id: json['id'] as String, + name: json['name'] as String, + profile_img_url: json['profile_img_url'] as String?, + banner_img_url: json['banner_img_url'] as String?, + type: json['type'] ?? TournamentType.other, + members: (json['members'] as List?) + ?.map((e) => TournamentMember.fromJson( + Map.from(e as Map))) + .toList() ?? + const [], + start_date: DateTime.parse(json['start_date'] as String), + end_date: DateTime.parse(json['end_date'] as String), + team_ids: (json['team_ids'] as List?) + ?.map((e) => e as String) + .toList() ?? + const [], + match_ids: (json['match_ids'] as List?) + ?.map((e) => e as String) + .toList() ?? + const [], + ); + +Map _$$TournamentModelImplToJson( + _$TournamentModelImpl instance) => + { + 'id': instance.id, + 'name': instance.name, + 'profile_img_url': instance.profile_img_url, + 'banner_img_url': instance.banner_img_url, + 'type': instance.type, + 'members': instance.members.map((e) => e.toJson()).toList(), + 'start_date': instance.start_date.toIso8601String(), + 'end_date': instance.end_date.toIso8601String(), + 'team_ids': instance.team_ids, + 'match_ids': instance.match_ids, + }; + +_$TournamentMemberImpl _$$TournamentMemberImplFromJson( + Map json) => + _$TournamentMemberImpl( + id: json['id'] as String, + role: $enumDecodeNullable(_$TournamentMemberRoleEnumMap, json['role']) ?? + TournamentMemberRole.admin, + ); + +Map _$$TournamentMemberImplToJson( + _$TournamentMemberImpl instance) => + { + 'id': instance.id, + 'role': _$TournamentMemberRoleEnumMap[instance.role]!, + }; + +const _$TournamentMemberRoleEnumMap = { + TournamentMemberRole.organizer: 'organizer', + TournamentMemberRole.admin: 'admin', +}; diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart new file mode 100644 index 00000000..54b5bbca --- /dev/null +++ b/data/lib/service/tournament/tournament_service.dart @@ -0,0 +1,34 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; + +import '../../api/tournament/tournament_model.dart'; +import '../../errors/app_error.dart'; +import '../../utils/constant/firestore_constant.dart'; + +final tournamentServiceProvider = Provider( + (ref) => TournamentService(FirebaseFirestore.instance), +); + +class TournamentService { + final FirebaseFirestore _firestore; + + TournamentService(this._firestore); + + CollectionReference get _tournamentCollection => + _firestore.collection(FireStoreConst.tournamentCollection).withConverter( + fromFirestore: TournamentModel.fromFireStore, + toFirestore: (TournamentModel tournament, _) => tournament.toJson(), + ); + + String get generateTeamId => _tournamentCollection.doc().id; + + Future updateTournament(TournamentModel tournament) async { + try { + await _tournamentCollection + .doc(tournament.id) + .set(tournament, SetOptions(merge: true)); + } catch (error, stack) { + throw AppError.fromError(error, stack); + } + } +} diff --git a/data/lib/utils/constant/firestore_constant.dart b/data/lib/utils/constant/firestore_constant.dart index 842f9f27..da75a3ba 100644 --- a/data/lib/utils/constant/firestore_constant.dart +++ b/data/lib/utils/constant/firestore_constant.dart @@ -7,6 +7,7 @@ class FireStoreConst { static const String usersCollection = "users"; static const String userSessionCollection = "user_sessions"; static const String supportCollection = "contact_support"; + static const String tournamentCollection = "tournaments"; // matches field const static const String id = "id"; diff --git a/khelo/assets/images/ic_tournaments.svg b/khelo/assets/images/ic_tournaments.svg new file mode 100644 index 00000000..df8bc04e --- /dev/null +++ b/khelo/assets/images/ic_tournaments.svg @@ -0,0 +1,4 @@ + + + + diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index dcff55e0..0d19c5a7 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -137,8 +137,13 @@ "@_MY_CRICKET": { }, - "my_game_teams_tab_title": "Teams", + "my_cricket_teams_tab_title": "Teams", "my_cricket_screen_title": "My Cricket", + "my_cricket_tournament_title": "Tournament", + + "@ADD_TOURNAMENT": { + }, + "add_tournament_screen_title": "Add Tournament", "add_team_screen_title": "Add Team", "add_team_add_as_member_description_text": "Add me as a team member", diff --git a/khelo/lib/components/profile_image_avatar.dart b/khelo/lib/components/profile_image_avatar.dart index 3dc55605..cec0b715 100644 --- a/khelo/lib/components/profile_image_avatar.dart +++ b/khelo/lib/components/profile_image_avatar.dart @@ -14,7 +14,11 @@ class ProfileImageAvatar extends StatelessWidget { final String? imageUrl; final String? filePath; final String? placeHolderImage; + final Color? placeHolderColor; final bool isLoading; + final BoxShape shape; + final Color? color; + final BorderRadius? borderRadius; final Function() onEditButtonTap; const ProfileImageAvatar({ @@ -23,7 +27,11 @@ class ProfileImageAvatar extends StatelessWidget { this.imageUrl, this.filePath, this.placeHolderImage, + this.placeHolderColor, required this.isLoading, + this.shape = BoxShape.circle, + this.borderRadius, + this.color, required this.onEditButtonTap, }); @@ -49,8 +57,9 @@ class ProfileImageAvatar extends StatelessWidget { width: size, alignment: Alignment.center, decoration: BoxDecoration( - shape: BoxShape.circle, - color: context.colorScheme.primary, + shape: shape, + borderRadius: borderRadius, + color: color ?? context.colorScheme.primary, image: (filePath != null) ? DecorationImage( image: FileImage(File(filePath!)), @@ -93,7 +102,7 @@ class ProfileImageAvatar extends StatelessWidget { height: size / 3, width: size / 3, colorFilter: ColorFilter.mode( - context.colorScheme.textInversePrimary, + placeHolderColor ?? context.colorScheme.textInversePrimary, BlendMode.srcATop, ), ); diff --git a/khelo/lib/gen/assets.gen.dart b/khelo/lib/gen/assets.gen.dart index bac6d093..865563d1 100644 --- a/khelo/lib/gen/assets.gen.dart +++ b/khelo/lib/gen/assets.gen.dart @@ -112,6 +112,9 @@ class $AssetsImagesGen { /// File path: assets/images/ic_time.svg String get icTime => 'assets/images/ic_time.svg'; + /// File path: assets/images/ic_tournaments.svg + String get icTournaments => 'assets/images/ic_tournaments.svg'; + /// File path: assets/images/ic_umpire.svg String get icUmpire => 'assets/images/ic_umpire.svg'; @@ -172,6 +175,7 @@ class $AssetsImagesGen { icStats, icTermsConditions, icTime, + icTournaments, icUmpire, introCricketDark, introCricketLight, diff --git a/khelo/lib/ui/app_route.dart b/khelo/lib/ui/app_route.dart index 0839b32b..e1633c77 100644 --- a/khelo/lib/ui/app_route.dart +++ b/khelo/lib/ui/app_route.dart @@ -22,6 +22,7 @@ import 'package:khelo/ui/flow/team/add_team_member/add_team_member_screen.dart'; import 'package:khelo/ui/flow/team/detail/make_admin/make_team_admin_screen.dart'; import 'package:khelo/ui/flow/team/detail/team_detail_screen.dart'; import 'package:khelo/ui/flow/team/search_team/search_team_screen.dart'; +import 'package:khelo/ui/flow/tournament/add/add_tournament_screen.dart'; import 'flow/home/view_all/home_view_all_screen.dart'; import 'flow/main/main_screen.dart'; import 'flow/settings/support/contact_support_screen.dart'; @@ -47,6 +48,7 @@ class AppRoute { static const pathMatchDetailTab = '/match-detail-tab'; static const pathSearchHome = "/search-home"; static const pathViewAll = "/view-all"; + static const pathAddTournament = "/add-tournament"; final String path; final String? name; @@ -158,6 +160,11 @@ class AppRoute { ), ); + static AppRoute addTournament() => AppRoute( + pathAddTournament, + builder: (_) => const AddTournamentScreen(), + ); + static AppRoute matchDetailTab({required String matchId}) => AppRoute( pathMatchDetailTab, builder: (_) => MatchDetailTabScreen( @@ -303,6 +310,10 @@ class AppRoute { path: pathAddMatch, builder: (context, state) => state.widget(context), ), + GoRoute( + path: pathAddTournament, + builder: (context, state) => state.widget(context), + ), GoRoute( path: pathMatchDetailTab, builder: (context, state) => state.widget(context), diff --git a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart index b24ddbf3..f4e9ff73 100644 --- a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart +++ b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart @@ -8,6 +8,7 @@ import 'package:khelo/ui/flow/matches/match_list_screen.dart'; import 'package:khelo/ui/flow/my_game/my_game_tab_view_model.dart'; import 'package:khelo/ui/flow/team/team_list_screen.dart'; import 'package:khelo/ui/flow/team/team_list_view_model.dart'; +import 'package:khelo/ui/flow/tournament/tournament_list_screen.dart'; import 'package:style/button/action_button.dart'; import 'package:style/button/tab_button.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -24,6 +25,7 @@ class _MyGameTabScreenState extends ConsumerState final List _tabs = [ const MatchListScreen(), const TeamListScreen(), + const TournamentListScreen(), ]; late PageController _controller; @@ -99,12 +101,20 @@ class _MyGameTabScreenState extends ConsumerState ), const SizedBox(width: 8), TabButton( - context.l10n.my_game_teams_tab_title, + context.l10n.my_cricket_teams_tab_title, selected: _selectedTab == 1, onTap: () { _controller.jumpToPage(1); }, ), + const SizedBox(width: 8), + TabButton( + context.l10n.my_cricket_tournament_title, + selected: _selectedTab == 2, + onTap: () { + _controller.jumpToPage(2); + }, + ), const Spacer(), if (_selectedTab == 1 && ref.watch(teamListViewStateProvider).teams.isNotEmpty) ...[ @@ -112,15 +122,23 @@ class _MyGameTabScreenState extends ConsumerState onPressed: () => ref .read(teamListViewStateProvider.notifier) .onFilterButtonTap(), - icon: Icon(CupertinoIcons.slider_horizontal_3, - color: context.colorScheme.textPrimary)), + icon: Icon( + CupertinoIcons.slider_horizontal_3, + color: context.colorScheme.textPrimary, + )), ], - actionButton(context, - onPressed: () => _selectedTab == 1 - ? AppRoute.addTeam().push(context) - : AppRoute.addMatch().push(context), - icon: Icon(Icons.add, color: context.colorScheme.textPrimary)), - const SizedBox(width: 8), + actionButton(context, onPressed: () { + final actions = [ + AppRoute.addMatch(), + AppRoute.addTeam(), + AppRoute.addTournament(), + ]; + actions[_selectedTab].push(context); + }, + icon: Icon( + Icons.add, + color: context.colorScheme.textPrimary, + )), ], ), ); diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart new file mode 100644 index 00000000..27dd382d --- /dev/null +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -0,0 +1,70 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/app_page.dart'; +import 'package:khelo/components/profile_image_avatar.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/gen/assets.gen.dart'; +import 'package:khelo/ui/flow/tournament/add/add_tournament_view_model.dart'; +import 'package:style/extensions/context_extensions.dart'; + +import '../../../../components/image_picker_sheet.dart'; + +class AddTournamentScreen extends ConsumerStatefulWidget { + const AddTournamentScreen({super.key}); + + @override + ConsumerState createState() => + _AddTournamentScreenState(); +} + +class _AddTournamentScreenState extends ConsumerState { + late AddTournamentViewNotifier notifier; + + @override + void initState() { + super.initState(); + notifier = ref.read(addTournamentStateProvider.notifier); + } + + @override + Widget build(BuildContext context) { + return AppPage( + title: context.l10n.add_tournament_screen_title, + body: Builder( + builder: (context) => _body(context), + ), + ); + } + + Widget _body(BuildContext context) { + final state = ref.watch(addTournamentStateProvider); + return ListView( + children: [ + _topHeader(context, state), + ], + ); + } + + Widget _topHeader(BuildContext context, AddTournamentState state) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 24.0), + child: ProfileImageAvatar( + size: 120, + isLoading: state.imageUploading && state.profileFilePath == null, + shape: BoxShape.rectangle, + filePath: state.profileFilePath, + imageUrl: state.profileImageUrl, + placeHolderImage: Assets.images.icTournaments, + placeHolderColor: context.colorScheme.textPrimary, + color: context.colorScheme.containerHigh, + borderRadius: BorderRadius.circular(16), + onEditButtonTap: () async { + final imagePath = await ImagePickerSheet.show(context, true); + if (imagePath != null) { + notifier.onImageChange(imagePath); + } + }, + ), + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart new file mode 100644 index 00000000..ca7e7370 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -0,0 +1,46 @@ +import 'dart:io'; + +import 'package:data/errors/app_error.dart'; +import 'package:flutter/material.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:khelo/domain/extensions/file_extension.dart'; + +part 'add_tournament_view_model.freezed.dart'; + +final addTournamentStateProvider = StateNotifierProvider.autoDispose< + AddTournamentViewNotifier, + AddTournamentState>((ref) => AddTournamentViewNotifier()); + +class AddTournamentViewNotifier extends StateNotifier { + AddTournamentViewNotifier() : super(const AddTournamentState()); + + Future onImageChange(String imagePath) async { + try { + state = state.copyWith(imageUploading: true, actionError: null); + if (await File(imagePath).isFileUnderMaxSize()) { + state = + state.copyWith(profileFilePath: imagePath, imageUploading: false); + } else { + state = state.copyWith( + imageUploading: false, + actionError: const LargeAttachmentUploadError()); + } + } catch (e) { + state = state.copyWith(imageUploading: false, actionError: e); + debugPrint("EditProfileViewNotifier: error while image upload -> $e"); + } + } +} + +@freezed +class AddTournamentState with _$AddTournamentState { + const factory AddTournamentState({ + Object? error, + Object? actionError, + @Default(false) bool imageUploading, + @Default(false) bool loading, + String? profileFilePath, + @Default(null) String? profileImageUrl, + }) = _AddTournamentState; +} diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart new file mode 100644 index 00000000..ee310a22 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -0,0 +1,250 @@ +// 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 'add_tournament_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 _$AddTournamentState { + Object? get error => throw _privateConstructorUsedError; + Object? get actionError => throw _privateConstructorUsedError; + bool get imageUploading => throw _privateConstructorUsedError; + bool get loading => throw _privateConstructorUsedError; + String? get profileFilePath => throw _privateConstructorUsedError; + String? get profileImageUrl => throw _privateConstructorUsedError; + + /// Create a copy of AddTournamentState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $AddTournamentStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $AddTournamentStateCopyWith<$Res> { + factory $AddTournamentStateCopyWith( + AddTournamentState value, $Res Function(AddTournamentState) then) = + _$AddTournamentStateCopyWithImpl<$Res, AddTournamentState>; + @useResult + $Res call( + {Object? error, + Object? actionError, + bool imageUploading, + bool loading, + String? profileFilePath, + String? profileImageUrl}); +} + +/// @nodoc +class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> + implements $AddTournamentStateCopyWith<$Res> { + _$AddTournamentStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of AddTournamentState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? actionError = freezed, + Object? imageUploading = null, + Object? loading = null, + Object? profileFilePath = freezed, + Object? profileImageUrl = freezed, + }) { + return _then(_value.copyWith( + error: freezed == error ? _value.error : error, + actionError: freezed == actionError ? _value.actionError : actionError, + imageUploading: null == imageUploading + ? _value.imageUploading + : imageUploading // ignore: cast_nullable_to_non_nullable + as bool, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + profileFilePath: freezed == profileFilePath + ? _value.profileFilePath + : profileFilePath // ignore: cast_nullable_to_non_nullable + as String?, + profileImageUrl: freezed == profileImageUrl + ? _value.profileImageUrl + : profileImageUrl // ignore: cast_nullable_to_non_nullable + as String?, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$AddTournamentStateImplCopyWith<$Res> + implements $AddTournamentStateCopyWith<$Res> { + factory _$$AddTournamentStateImplCopyWith(_$AddTournamentStateImpl value, + $Res Function(_$AddTournamentStateImpl) then) = + __$$AddTournamentStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Object? error, + Object? actionError, + bool imageUploading, + bool loading, + String? profileFilePath, + String? profileImageUrl}); +} + +/// @nodoc +class __$$AddTournamentStateImplCopyWithImpl<$Res> + extends _$AddTournamentStateCopyWithImpl<$Res, _$AddTournamentStateImpl> + implements _$$AddTournamentStateImplCopyWith<$Res> { + __$$AddTournamentStateImplCopyWithImpl(_$AddTournamentStateImpl _value, + $Res Function(_$AddTournamentStateImpl) _then) + : super(_value, _then); + + /// Create a copy of AddTournamentState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? actionError = freezed, + Object? imageUploading = null, + Object? loading = null, + Object? profileFilePath = freezed, + Object? profileImageUrl = freezed, + }) { + return _then(_$AddTournamentStateImpl( + error: freezed == error ? _value.error : error, + actionError: freezed == actionError ? _value.actionError : actionError, + imageUploading: null == imageUploading + ? _value.imageUploading + : imageUploading // ignore: cast_nullable_to_non_nullable + as bool, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + profileFilePath: freezed == profileFilePath + ? _value.profileFilePath + : profileFilePath // ignore: cast_nullable_to_non_nullable + as String?, + profileImageUrl: freezed == profileImageUrl + ? _value.profileImageUrl + : profileImageUrl // ignore: cast_nullable_to_non_nullable + as String?, + )); + } +} + +/// @nodoc + +class _$AddTournamentStateImpl implements _AddTournamentState { + const _$AddTournamentStateImpl( + {this.error, + this.actionError, + this.imageUploading = false, + this.loading = false, + this.profileFilePath, + this.profileImageUrl = null}); + + @override + final Object? error; + @override + final Object? actionError; + @override + @JsonKey() + final bool imageUploading; + @override + @JsonKey() + final bool loading; + @override + final String? profileFilePath; + @override + @JsonKey() + final String? profileImageUrl; + + @override + String toString() { + return 'AddTournamentState(error: $error, actionError: $actionError, imageUploading: $imageUploading, loading: $loading, profileFilePath: $profileFilePath, profileImageUrl: $profileImageUrl)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$AddTournamentStateImpl && + const DeepCollectionEquality().equals(other.error, error) && + const DeepCollectionEquality() + .equals(other.actionError, actionError) && + (identical(other.imageUploading, imageUploading) || + other.imageUploading == imageUploading) && + (identical(other.loading, loading) || other.loading == loading) && + (identical(other.profileFilePath, profileFilePath) || + other.profileFilePath == profileFilePath) && + (identical(other.profileImageUrl, profileImageUrl) || + other.profileImageUrl == profileImageUrl)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(error), + const DeepCollectionEquality().hash(actionError), + imageUploading, + loading, + profileFilePath, + profileImageUrl); + + /// Create a copy of AddTournamentState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$AddTournamentStateImplCopyWith<_$AddTournamentStateImpl> get copyWith => + __$$AddTournamentStateImplCopyWithImpl<_$AddTournamentStateImpl>( + this, _$identity); +} + +abstract class _AddTournamentState implements AddTournamentState { + const factory _AddTournamentState( + {final Object? error, + final Object? actionError, + final bool imageUploading, + final bool loading, + final String? profileFilePath, + final String? profileImageUrl}) = _$AddTournamentStateImpl; + + @override + Object? get error; + @override + Object? get actionError; + @override + bool get imageUploading; + @override + bool get loading; + @override + String? get profileFilePath; + @override + String? get profileImageUrl; + + /// Create a copy of AddTournamentState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$AddTournamentStateImplCopyWith<_$AddTournamentStateImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart new file mode 100644 index 00000000..a49e8b55 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -0,0 +1,26 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/app_page.dart'; + +class TournamentListScreen extends ConsumerStatefulWidget { + const TournamentListScreen({super.key}); + + @override + ConsumerState createState() => + _TournamentListScreenState(); +} + +class _TournamentListScreenState extends ConsumerState { + @override + Widget build(BuildContext context) { + return AppPage( + body: Builder( + builder: (context) => _body(context), + ), + ); + } + + Widget _body(BuildContext context) { + return const Column(); + } +} diff --git a/style/lib/button/action_button.dart b/style/lib/button/action_button.dart index 1cae9d9c..76e15b89 100644 --- a/style/lib/button/action_button.dart +++ b/style/lib/button/action_button.dart @@ -20,6 +20,9 @@ Widget actionButton( onPressed: onPressed, icon: icon, padding: padding, + style: const ButtonStyle( + tapTargetSize: MaterialTapTargetSize.shrinkWrap, + ), ); } } From 74ed90c447a8833a8dbd22e0b1b2ed6003fb2c57 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 27 Sep 2024 11:14:03 +0530 Subject: [PATCH 02/33] Added more functionality --- data/.flutter-plugins | 46 +-- data/.flutter-plugins-dependencies | 2 +- data/lib/api/tournament/tournament_model.dart | 12 +- .../tournament/tournament_model.freezed.dart | 131 ++++--- .../api/tournament/tournament_model.g.dart | 46 ++- .../constant/firebase_storage_constant.dart | 6 + data/pubspec.yaml | 2 +- khelo/assets/locales/app_en.arb | 46 ++- khelo/lib/components/action_bottom_sheet.dart | 65 ++-- .../domain/extensions/enum_extensions.dart | 49 +++ .../matches/add_match/add_match_screen.dart | 81 +---- .../edit_profile/edit_profile_screen.dart | 38 +- .../tournament/add/add_tournament_screen.dart | 237 ++++++++++++- .../add/add_tournament_view_model.dart | 154 ++++++++- .../add_tournament_view_model.freezed.dart | 326 +++++++++++++++--- style/lib/pickers/date_and_time_picker.dart | 71 ++++ 16 files changed, 1030 insertions(+), 282 deletions(-) create mode 100644 style/lib/pickers/date_and_time_picker.dart diff --git a/data/.flutter-plugins b/data/.flutter-plugins index dfe2d5b8..82c06fd7 100644 --- a/data/.flutter-plugins +++ b/data/.flutter-plugins @@ -1,24 +1,24 @@ # This is a generated file; do not edit or check into version control. -cloud_firestore=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore-5.0.2/ -cloud_firestore_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.0.2/ -cloud_functions=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/ -cloud_functions_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.0/ -device_info_plus=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/ -firebase_auth=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth-5.1.1/ -firebase_auth_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.0/ -firebase_core=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/ -firebase_core_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.0/ -firebase_messaging=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.2/ -firebase_messaging_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.0/ -firebase_storage=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage-12.1.0/ -firebase_storage_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.0/ -flutter_timezone=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/flutter_timezone-2.0.1/ -package_info_plus=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/ -path_provider_linux=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ -path_provider_windows=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/ -shared_preferences=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences-2.2.3/ -shared_preferences_android=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.3/ -shared_preferences_foundation=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/ -shared_preferences_linux=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/ -shared_preferences_web=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_web-2.2.1/ -shared_preferences_windows=/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/ +cloud_firestore=/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.2.0/ +cloud_firestore_web=/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.1.0/ +cloud_functions=/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/ +cloud_functions_web=/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.0/ +device_info_plus=/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/ +firebase_auth=/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.1.3/ +firebase_auth_web=/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.0/ +firebase_core=/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/ +firebase_core_web=/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.0/ +firebase_messaging=/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.4/ +firebase_messaging_web=/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.0/ +firebase_storage=/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.1.2/ +firebase_storage_web=/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.0/ +flutter_timezone=/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/ +package_info_plus=/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/ +path_provider_linux=/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ +path_provider_windows=/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/ +shared_preferences=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences-2.3.1/ +shared_preferences_android=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.0/ +shared_preferences_foundation=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.0/ +shared_preferences_linux=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.0/ +shared_preferences_web=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.1/ +shared_preferences_windows=/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.0/ diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index 0841a659..b1c10355 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore-5.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage-12.1.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/flutter_timezone-2.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore-5.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage-12.1.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/flutter_timezone-2.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore-5.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage-12.1.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/flutter_timezone-2.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.4.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore-5.0.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage-12.1.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.0.2/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.0/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.0/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.0/","dependencies":[]},{"name":"firebase_messaging_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.0/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.0/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/flutter_timezone-2.0.1/","dependencies":[]},{"name":"package_info_plus","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/sidhdhi.p/.pub-cache/hosted/pub.dev/shared_preferences_web-2.2.1/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-09-16 11:42:12.161649","version":"3.24.1","swift_package_manager_enabled":false} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.2.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.1.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.2.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.1.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.0/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.2.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.0.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.1.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.0/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.0/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.2.0/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.4.1/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.1.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.0/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.1.0/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.0/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.1/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.0/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.0/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.0/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.0/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-2.1.0/","dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.1/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-09-25 17:46:19.879290","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index 950e99f1..4e2b4f90 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -2,8 +2,8 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; -import 'package:json_serializable/type_helper.dart'; +import '../../converter/timestamp_json_converter.dart'; import '../match/match_model.dart'; import '../team/team_model.dart'; @@ -18,11 +18,12 @@ class TournamentModel with _$TournamentModel { required String id, required String name, String? profile_img_url, - String? banner_img_url, - @Default(TournamentType.other) type, + required TournamentType type, @Default([]) List members, - @JsonConverterHelper() required DateTime start_date, - @JsonConverterHelper() required DateTime end_date, + required String created_by, + @TimeStampJsonConverter() DateTime? created_at, + @TimeStampJsonConverter() required DateTime start_date, + @TimeStampJsonConverter() required DateTime end_date, @Default([]) List team_ids, @Default([]) List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -54,6 +55,7 @@ class TournamentMember with _$TournamentMember { _$TournamentMemberFromJson(json); } +@JsonEnum(valueField: "value") enum TournamentType { knockOut(1), miniRobin(2), diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index c9c0ab83..a9e593d1 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -23,12 +23,14 @@ mixin _$TournamentModel { String get id => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; String? get profile_img_url => throw _privateConstructorUsedError; - String? get banner_img_url => throw _privateConstructorUsedError; - dynamic get type => throw _privateConstructorUsedError; + TournamentType get type => throw _privateConstructorUsedError; List get members => throw _privateConstructorUsedError; - @JsonConverterHelper() + String get created_by => throw _privateConstructorUsedError; + @TimeStampJsonConverter() + DateTime? get created_at => throw _privateConstructorUsedError; + @TimeStampJsonConverter() DateTime get start_date => throw _privateConstructorUsedError; - @JsonConverterHelper() + @TimeStampJsonConverter() DateTime get end_date => throw _privateConstructorUsedError; List get team_ids => throw _privateConstructorUsedError; List get match_ids => throw _privateConstructorUsedError; @@ -53,11 +55,12 @@ abstract class $TournamentModelCopyWith<$Res> { {String id, String name, String? profile_img_url, - String? banner_img_url, - dynamic type, + TournamentType type, List members, - @JsonConverterHelper() DateTime start_date, - @JsonConverterHelper() DateTime end_date, + String created_by, + @TimeStampJsonConverter() DateTime? created_at, + @TimeStampJsonConverter() DateTime start_date, + @TimeStampJsonConverter() DateTime end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -82,9 +85,10 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> Object? id = null, Object? name = null, Object? profile_img_url = freezed, - Object? banner_img_url = freezed, - Object? type = freezed, + Object? type = null, Object? members = null, + Object? created_by = null, + Object? created_at = freezed, Object? start_date = null, Object? end_date = null, Object? team_ids = null, @@ -105,18 +109,22 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> ? _value.profile_img_url : profile_img_url // ignore: cast_nullable_to_non_nullable as String?, - banner_img_url: freezed == banner_img_url - ? _value.banner_img_url - : banner_img_url // ignore: cast_nullable_to_non_nullable - as String?, - type: freezed == type + type: null == type ? _value.type : type // ignore: cast_nullable_to_non_nullable - as dynamic, + as TournamentType, members: null == members ? _value.members : members // ignore: cast_nullable_to_non_nullable as List, + created_by: null == created_by + ? _value.created_by + : created_by // ignore: cast_nullable_to_non_nullable + as String, + created_at: freezed == created_at + ? _value.created_at + : created_at // ignore: cast_nullable_to_non_nullable + as DateTime?, start_date: null == start_date ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable @@ -157,11 +165,12 @@ abstract class _$$TournamentModelImplCopyWith<$Res> {String id, String name, String? profile_img_url, - String? banner_img_url, - dynamic type, + TournamentType type, List members, - @JsonConverterHelper() DateTime start_date, - @JsonConverterHelper() DateTime end_date, + String created_by, + @TimeStampJsonConverter() DateTime? created_at, + @TimeStampJsonConverter() DateTime start_date, + @TimeStampJsonConverter() DateTime end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -184,9 +193,10 @@ class __$$TournamentModelImplCopyWithImpl<$Res> Object? id = null, Object? name = null, Object? profile_img_url = freezed, - Object? banner_img_url = freezed, - Object? type = freezed, + Object? type = null, Object? members = null, + Object? created_by = null, + Object? created_at = freezed, Object? start_date = null, Object? end_date = null, Object? team_ids = null, @@ -207,15 +217,22 @@ class __$$TournamentModelImplCopyWithImpl<$Res> ? _value.profile_img_url : profile_img_url // ignore: cast_nullable_to_non_nullable as String?, - banner_img_url: freezed == banner_img_url - ? _value.banner_img_url - : banner_img_url // ignore: cast_nullable_to_non_nullable - as String?, - type: freezed == type ? _value.type! : type, + type: null == type + ? _value.type + : type // ignore: cast_nullable_to_non_nullable + as TournamentType, members: null == members ? _value._members : members // ignore: cast_nullable_to_non_nullable as List, + created_by: null == created_by + ? _value.created_by + : created_by // ignore: cast_nullable_to_non_nullable + as String, + created_at: freezed == created_at + ? _value.created_at + : created_at // ignore: cast_nullable_to_non_nullable + as DateTime?, start_date: null == start_date ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable @@ -252,11 +269,12 @@ class _$TournamentModelImpl implements _TournamentModel { {required this.id, required this.name, this.profile_img_url, - this.banner_img_url, - this.type = TournamentType.other, + required this.type, final List members = const [], - @JsonConverterHelper() required this.start_date, - @JsonConverterHelper() required this.end_date, + required this.created_by, + @TimeStampJsonConverter() this.created_at, + @TimeStampJsonConverter() required this.start_date, + @TimeStampJsonConverter() required this.end_date, final List team_ids = const [], final List match_ids = const [], @JsonKey(includeFromJson: false, includeToJson: false) @@ -279,10 +297,7 @@ class _$TournamentModelImpl implements _TournamentModel { @override final String? profile_img_url; @override - final String? banner_img_url; - @override - @JsonKey() - final dynamic type; + final TournamentType type; final List _members; @override @JsonKey() @@ -293,10 +308,15 @@ class _$TournamentModelImpl implements _TournamentModel { } @override - @JsonConverterHelper() + final String created_by; + @override + @TimeStampJsonConverter() + final DateTime? created_at; + @override + @TimeStampJsonConverter() final DateTime start_date; @override - @JsonConverterHelper() + @TimeStampJsonConverter() final DateTime end_date; final List _team_ids; @override @@ -336,7 +356,7 @@ class _$TournamentModelImpl implements _TournamentModel { @override String toString() { - return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, banner_img_url: $banner_img_url, type: $type, members: $members, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; + return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, type: $type, members: $members, created_by: $created_by, created_at: $created_at, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; } @override @@ -348,10 +368,12 @@ class _$TournamentModelImpl implements _TournamentModel { (identical(other.name, name) || other.name == name) && (identical(other.profile_img_url, profile_img_url) || other.profile_img_url == profile_img_url) && - (identical(other.banner_img_url, banner_img_url) || - other.banner_img_url == banner_img_url) && - const DeepCollectionEquality().equals(other.type, type) && + (identical(other.type, type) || other.type == type) && const DeepCollectionEquality().equals(other._members, _members) && + (identical(other.created_by, created_by) || + other.created_by == created_by) && + (identical(other.created_at, created_at) || + other.created_at == created_at) && (identical(other.start_date, start_date) || other.start_date == start_date) && (identical(other.end_date, end_date) || @@ -370,9 +392,10 @@ class _$TournamentModelImpl implements _TournamentModel { id, name, profile_img_url, - banner_img_url, - const DeepCollectionEquality().hash(type), + type, const DeepCollectionEquality().hash(_members), + created_by, + created_at, start_date, end_date, const DeepCollectionEquality().hash(_team_ids), @@ -400,11 +423,12 @@ abstract class _TournamentModel implements TournamentModel { {required final String id, required final String name, final String? profile_img_url, - final String? banner_img_url, - final dynamic type, + required final TournamentType type, final List members, - @JsonConverterHelper() required final DateTime start_date, - @JsonConverterHelper() required final DateTime end_date, + required final String created_by, + @TimeStampJsonConverter() final DateTime? created_at, + @TimeStampJsonConverter() required final DateTime start_date, + @TimeStampJsonConverter() required final DateTime end_date, final List team_ids, final List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -422,16 +446,19 @@ abstract class _TournamentModel implements TournamentModel { @override String? get profile_img_url; @override - String? get banner_img_url; - @override - dynamic get type; + TournamentType get type; @override List get members; @override - @JsonConverterHelper() + String get created_by; + @override + @TimeStampJsonConverter() + DateTime? get created_at; + @override + @TimeStampJsonConverter() DateTime get start_date; @override - @JsonConverterHelper() + @TimeStampJsonConverter() DateTime get end_date; @override List get team_ids; diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index a0e932ad..76d2270c 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -11,15 +11,19 @@ _$TournamentModelImpl _$$TournamentModelImplFromJson(Map json) => id: json['id'] as String, name: json['name'] as String, profile_img_url: json['profile_img_url'] as String?, - banner_img_url: json['banner_img_url'] as String?, - type: json['type'] ?? TournamentType.other, + type: $enumDecode(_$TournamentTypeEnumMap, json['type']), members: (json['members'] as List?) ?.map((e) => TournamentMember.fromJson( Map.from(e as Map))) .toList() ?? const [], - start_date: DateTime.parse(json['start_date'] as String), - end_date: DateTime.parse(json['end_date'] as String), + created_by: json['created_by'] as String, + created_at: _$JsonConverterFromJson( + json['created_at'], const TimeStampJsonConverter().fromJson), + start_date: + const TimeStampJsonConverter().fromJson(json['start_date'] as Object), + end_date: + const TimeStampJsonConverter().fromJson(json['end_date'] as Object), team_ids: (json['team_ids'] as List?) ?.map((e) => e as String) .toList() ?? @@ -36,15 +40,41 @@ Map _$$TournamentModelImplToJson( 'id': instance.id, 'name': instance.name, 'profile_img_url': instance.profile_img_url, - 'banner_img_url': instance.banner_img_url, - 'type': instance.type, + 'type': _$TournamentTypeEnumMap[instance.type]!, 'members': instance.members.map((e) => e.toJson()).toList(), - 'start_date': instance.start_date.toIso8601String(), - 'end_date': instance.end_date.toIso8601String(), + 'created_by': instance.created_by, + 'created_at': _$JsonConverterToJson( + instance.created_at, const TimeStampJsonConverter().toJson), + 'start_date': const TimeStampJsonConverter().toJson(instance.start_date), + 'end_date': const TimeStampJsonConverter().toJson(instance.end_date), 'team_ids': instance.team_ids, 'match_ids': instance.match_ids, }; +const _$TournamentTypeEnumMap = { + TournamentType.knockOut: 1, + TournamentType.miniRobin: 2, + TournamentType.boxLeague: 3, + TournamentType.doubleOut: 4, + TournamentType.superOver: 5, + TournamentType.bestOf: 6, + TournamentType.gully: 7, + TournamentType.mixed: 8, + TournamentType.other: 9, +}; + +Value? _$JsonConverterFromJson( + Object? json, + Value? Function(Json json) fromJson, +) => + json == null ? null : fromJson(json as Json); + +Json? _$JsonConverterToJson( + Value? value, + Json? Function(Value value) toJson, +) => + value == null ? null : toJson(value); + _$TournamentMemberImpl _$$TournamentMemberImplFromJson( Map json) => _$TournamentMemberImpl( diff --git a/data/lib/utils/constant/firebase_storage_constant.dart b/data/lib/utils/constant/firebase_storage_constant.dart index 053f8109..5130b97d 100644 --- a/data/lib/utils/constant/firebase_storage_constant.dart +++ b/data/lib/utils/constant/firebase_storage_constant.dart @@ -17,4 +17,10 @@ class StorageConst { required String imageName, }) => "$rootDirectory/$userId/support_attachment_images/$imageName"; + + static String tournamentProfileUploadPath({ + required String userId, + required String tournamentId, + }) => + "$rootDirectory/$userId/tournament_profile_images/$tournamentId"; } diff --git a/data/pubspec.yaml b/data/pubspec.yaml index efb59127..6f46bdc5 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: dio: ^5.0.2 cloud_functions: ^5.1.1 json_annotation: ^4.9.0 - json_serializable: ^6.7.1 + json_serializable: ^6.8.0 freezed_annotation: ^2.4.1 stream_transform: ^2.1.0 diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 4db899a2..4f7095f6 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -18,6 +18,7 @@ "common_retry_title": "Retry", "common_submit_title": "Submit", "common_not_now": "Not now", + "common_create": "Create", "common_not_specified_title": "Not Specified", "common_runs_title": "{count, plural, =0{{count} runs} =1{{count} run} other{{count} runs}}", "@common_runs_title": { @@ -141,9 +142,52 @@ "my_cricket_screen_title": "My Cricket", "my_cricket_tournament_title": "Tournament", - "@ADD_TOURNAMENT": { + "@_ADD_TOURNAMENT": { }, "add_tournament_screen_title": "Add Tournament", + "add_tournament_name": "Tournament Name", + "add_tournament_type": "Tournament Type", + "add_tournament_start_date": "Start Date", + "add_tournament_end_date": "End Date", + "add_tournament_team_selection": "Team selection", + "add_tournament_matches_selection": "Matches selection", + "add_tournament_team_count": "{count, plural, =0{Select Team} =1{{count} Team} other{{count} Teams}}", + "@add_tournament_team_count": { + "placeholders": { + "count": {} + } + }, + "add_tournament_match_count": "{count, plural, =0{Select Match} =1{{count} Match} other{{count} Matches}}", + "@add_tournament_match_count": { + "placeholders": { + "count": {} + } + }, + + + "@_TOURNAMENT_TYPE":{ + }, + "tournament_type_knockout": "KnockOut", + "tournament_type_miniRobin": "Mini Robin", + "tournament_type_boxLeague": "Box League", + "tournament_type_doubleOut": "Double Out", + "tournament_type_superOver": "Super Over", + "tournament_type_bestOf": "Best Of", + "tournament_type_gully": "Gully", + "tournament_type_mixed": "Mixed", + "tournament_type_other": "Other", + + "tournament_type_knockout_description": "Teams face off in a single elimination, with the loser immediately knocked out.", + "tournament_type_miniRobin_description": "A smaller round-robin format where each team plays once against all others.", + "tournament_type_boxLeague_description": "Teams are divided into groups, with top teams advancing to the knockout stage.", + "tournament_type_doubleOut_description": "Teams get two chances before being knocked out, with a winners and losers bracket.", + "tournament_type_superOver_description": "A knockout format with a super over to decide tied matches.", + "tournament_type_bestOf_description": "Teams play a series of matches, and the first to win the majority is the champion.", + "tournament_type_gully_description": "Casual street-style cricket with short games and flexible rules.", + "tournament_type_mixed_description": "Teams are randomly mixed, creating fun and unpredictable matches", + "tournament_type_other_description": "A custom format for tournaments with unique rules or structures.", + + "add_team_screen_title": "Add Team", "add_team_add_as_member_description_text": "Add me as a team member", diff --git a/khelo/lib/components/action_bottom_sheet.dart b/khelo/lib/components/action_bottom_sheet.dart index 5a257856..2fad8ee9 100644 --- a/khelo/lib/components/action_bottom_sheet.dart +++ b/khelo/lib/components/action_bottom_sheet.dart @@ -10,6 +10,7 @@ Future showActionBottomSheet({ required List items, bool useRootNavigator = true, bool showDragHandle = true, + double? heightFactor, }) async { HapticFeedback.mediumImpact(); return await showModalBottomSheet( @@ -22,27 +23,31 @@ Future showActionBottomSheet({ useRootNavigator: useRootNavigator, isScrollControlled: true, showDragHandle: showDragHandle, + useSafeArea: true, context: context, - builder: (context) => SingleChildScrollView( - child: Container( - decoration: BoxDecoration( - borderRadius: const BorderRadius.vertical( - top: Radius.circular(16), + builder: (context) => FractionallySizedBox( + heightFactor: heightFactor, + child: SingleChildScrollView( + child: Container( + decoration: BoxDecoration( + borderRadius: const BorderRadius.vertical( + top: Radius.circular(16), + ), + color: context.colorScheme.surface, ), - color: context.colorScheme.surface, - ), - padding: EdgeInsets.only( - bottom: context.mediaQueryPadding.bottom, - ), - child: ColumnBuilder.separated( - separatorBuilder: (index) => Divider( - height: 0, - thickness: 1, - color: context.colorScheme.outline, + padding: EdgeInsets.only( + bottom: context.mediaQueryPadding.bottom, + ), + child: ColumnBuilder.separated( + separatorBuilder: (index) => Divider( + height: 0, + thickness: 1, + color: context.colorScheme.outline, + ), + itemBuilder: (index) => items[index], + itemCount: items.length, + mainAxisSize: MainAxisSize.min, ), - itemBuilder: (index) => items[index], - itemCount: items.length, - mainAxisSize: MainAxisSize.min, ), ), ), @@ -54,6 +59,7 @@ class BottomSheetAction extends StatelessWidget { final String title; final Widget? child; final bool enabled; + final String? subTitle; final void Function()? onTap; const BottomSheetAction({ @@ -62,6 +68,7 @@ class BottomSheetAction extends StatelessWidget { required this.title, this.enabled = true, this.child, + this.subTitle, this.onTap, }); @@ -80,10 +87,24 @@ class BottomSheetAction extends StatelessWidget { child: const SizedBox(width: 20), ), Expanded( - child: Text( - title, - style: AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + title, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + if (subTitle != null) ...[ + const SizedBox(height: 4), + Text( + subTitle ?? '', + style: AppTextStyle.caption.copyWith( + color: context.colorScheme.textSecondary, + ), + ), + ], + ], ), ), Visibility( diff --git a/khelo/lib/domain/extensions/enum_extensions.dart b/khelo/lib/domain/extensions/enum_extensions.dart index 85b28b7c..7901e6b0 100644 --- a/khelo/lib/domain/extensions/enum_extensions.dart +++ b/khelo/lib/domain/extensions/enum_extensions.dart @@ -1,5 +1,6 @@ import 'package:data/api/ball_score/ball_score_model.dart'; import 'package:data/api/match/match_model.dart'; +import 'package:data/api/tournament/tournament_model.dart'; import 'package:data/api/user/user_models.dart'; import 'package:flutter/cupertino.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; @@ -265,3 +266,51 @@ extension WinnerByTypeString on WinnerByType { } } } + +extension TournamentTypeString on TournamentType { + String getString(BuildContext context) { + switch (this) { + case TournamentType.knockOut: + return context.l10n.tournament_type_knockout; + case TournamentType.miniRobin: + return context.l10n.tournament_type_miniRobin; + case TournamentType.boxLeague: + return context.l10n.tournament_type_boxLeague; + case TournamentType.doubleOut: + return context.l10n.tournament_type_doubleOut; + case TournamentType.superOver: + return context.l10n.tournament_type_superOver; + case TournamentType.bestOf: + return context.l10n.tournament_type_bestOf; + case TournamentType.gully: + return context.l10n.tournament_type_gully; + case TournamentType.mixed: + return context.l10n.tournament_type_mixed; + case TournamentType.other: + return context.l10n.tournament_type_other; + } + } + + String getDescriptionString(BuildContext context) { + switch (this) { + case TournamentType.knockOut: + return context.l10n.tournament_type_knockout_description; + case TournamentType.miniRobin: + return context.l10n.tournament_type_miniRobin_description; + case TournamentType.boxLeague: + return context.l10n.tournament_type_boxLeague_description; + case TournamentType.doubleOut: + return context.l10n.tournament_type_doubleOut_description; + case TournamentType.superOver: + return context.l10n.tournament_type_superOver_description; + case TournamentType.bestOf: + return context.l10n.tournament_type_bestOf_description; + case TournamentType.gully: + return context.l10n.tournament_type_gully_description; + case TournamentType.mixed: + return context.l10n.tournament_type_mixed_description; + case TournamentType.other: + return context.l10n.tournament_type_other_description; + } + } +} 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 8bf1a4ab..21f6a5aa 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 @@ -27,10 +27,10 @@ import 'package:style/button/bottom_sticky_overlay.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; +import 'package:style/pickers/date_and_time_picker.dart'; import 'package:style/text/app_text_field.dart'; import 'package:style/text/app_text_style.dart'; import 'package:data/api/match/match_model.dart'; -import 'package:style/theme/colors.dart'; import 'package:style/widgets/adaptive_outlined_tile.dart'; class AddMatchScreen extends ConsumerStatefulWidget { @@ -314,7 +314,12 @@ class _AddMatchScreenState extends ConsumerState { headerText: context.l10n.add_match_date_title, placeholder: context.l10n.add_match_date_title, onTap: () { - _selectDate(context, notifier, state); + selectDate( + context, + initialDate: state.matchTime, + onDateSelected: (selectedDate) => + notifier.onDateSelect(selectedDate: selectedDate), + ); }), ), const SizedBox(width: 16), @@ -325,7 +330,12 @@ class _AddMatchScreenState extends ConsumerState { headerText: context.l10n.add_match_time_title, placeholder: context.l10n.add_match_time_title, onTap: () { - _selectTime(context, notifier, state); + selectDate( + context, + initialDate: state.matchTime, + onDateSelected: (selectedDate) => + notifier.onDateSelect(selectedDate: selectedDate), + ); }), ), ], @@ -335,71 +345,6 @@ class _AddMatchScreenState extends ConsumerState { ); } - Future _selectDate( - BuildContext context, - AddMatchViewNotifier notifier, - AddMatchViewState state, - ) async { - showDatePicker( - context: context, - initialDate: state.matchTime, - firstDate: DateTime(1965), - lastDate: DateTime(2101), - builder: (context, child) { - return Theme( - data: context.brightness == Brightness.dark - ? materialThemeDataDark - : materialThemeDataLight, - child: child!, - ); - }, - ).then((selectedDate) { - if (selectedDate != null) { - DateTime selectedDateTime = DateTime( - selectedDate.year, - selectedDate.month, - selectedDate.day, - state.matchTime.hour, - state.matchTime.minute, - ); - notifier.onDateSelect(selectedDate: selectedDateTime); - } - }); - } - - Future _selectTime( - BuildContext context, - AddMatchViewNotifier notifier, - AddMatchViewState state, - ) async { - showTimePicker( - context: context, - initialTime: TimeOfDay( - hour: state.matchTime.hour, - minute: state.matchTime.minute, - ), - builder: (context, child) { - return Theme( - data: context.brightness == Brightness.dark - ? materialThemeDataDark - : materialThemeDataLight, - child: child!, - ); - }, - ).then((selectedTime) { - if (selectedTime != null) { - DateTime selectedDateTime = DateTime( - state.matchTime.year, - state.matchTime.month, - state.matchTime.day, - selectedTime.hour, - selectedTime.minute, - ); - notifier.onDateSelect(selectedDate: selectedDateTime); - } - }); - } - void _showDeleteAlert( BuildContext context, { required Function() onDelete, 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 f145da52..ab1620d7 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 @@ -18,9 +18,9 @@ import 'package:khelo/ui/flow/settings/edit_profile/edit_profile_view_model.dart import 'package:style/button/action_button.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/pickers/date_and_time_picker.dart'; import 'package:style/text/app_text_field.dart'; import 'package:style/text/app_text_style.dart'; -import 'package:style/theme/colors.dart'; import 'package:style/widgets/adaptive_outlined_tile.dart'; import '../../../../components/image_picker_sheet.dart'; @@ -171,7 +171,16 @@ class EditProfileScreen extends ConsumerWidget { title: state.dob.format(context, DateFormatType.shortDate), showTrailingIcon: true, placeholder: context.l10n.edit_profile_dob_placeholder, - onTap: () => _selectDate(context, notifier, state), + onTap: () => selectDate( + context, + helpText: context.l10n.edit_profile_select_birth_date_placeholder, + initialDate: state.dob, + onDateSelected: (date) { + if (date != state.dob) { + notifier.onDateSelect(selectedDate: date); + } + }, + ), ), ), const SizedBox(width: 16), @@ -296,31 +305,6 @@ class EditProfileScreen extends ConsumerWidget { : null; } - 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, - initialDate: state.dob, - firstDate: DateTime(1920), - lastDate: DateTime.now(), - builder: (context, child) { - return Theme( - data: context.brightness == Brightness.dark - ? materialThemeDataDark - : materialThemeDataLight, - child: child!, - ); - }, - ); - if (picked != null && picked != state.dob) { - notifier.onDateSelect(selectedDate: picked); - } - } - Widget _deleteButton( BuildContext context, { required Function() onDelete, diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index 27dd382d..98cd621f 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -1,13 +1,27 @@ +import 'package:data/api/tournament/tournament_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; -import 'package:khelo/components/app_page.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; import 'package:khelo/components/profile_image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/gen/assets.gen.dart'; import 'package:khelo/ui/flow/tournament/add/add_tournament_view_model.dart'; +import 'package:style/animations/on_tap_scale.dart'; +import 'package:style/button/bottom_sticky_overlay.dart'; +import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/pickers/date_and_time_picker.dart'; +import 'package:style/text/app_text_field.dart'; +import 'package:style/text/app_text_style.dart'; +import 'package:style/widgets/adaptive_outlined_tile.dart'; +import '../../../../components/action_bottom_sheet.dart'; +import '../../../../components/app_page.dart'; +import '../../../../components/error_snackbar.dart'; import '../../../../components/image_picker_sheet.dart'; +import '../../../../domain/formatter/date_formatter.dart'; class AddTournamentScreen extends ConsumerStatefulWidget { const AddTournamentScreen({super.key}); @@ -26,34 +40,118 @@ class _AddTournamentScreenState extends ConsumerState { notifier = ref.read(addTournamentStateProvider.notifier); } + void _observeActionError(BuildContext context, WidgetRef ref) { + ref.listen(addTournamentStateProvider.select((value) => value.actionError), + (previous, next) { + if (next != null) { + showErrorSnackBar(context: context, error: next); + } + }); + } + + void _observePop(BuildContext context, WidgetRef ref) { + ref.listen(addTournamentStateProvider.select((value) => value.pop), + (previous, next) { + if (next) { + context.pop(); + } + }); + } + @override Widget build(BuildContext context) { + _observeActionError(context, ref); + _observePop(context, ref); + + final state = ref.watch(addTournamentStateProvider); + return AppPage( title: context.l10n.add_tournament_screen_title, body: Builder( - builder: (context) => _body(context), + builder: (context) => Stack( + children: [ + _body(context, state), + _stickyButton(context, state), + ], + ), ), ); } - Widget _body(BuildContext context) { - final state = ref.watch(addTournamentStateProvider); + Widget _body(BuildContext context, AddTournamentState state) { return ListView( + padding: context.mediaQueryPadding + const EdgeInsets.all(16), children: [ - _topHeader(context, state), + _profileView(context, state), + AppTextField( + controller: state.nameController, + label: context.l10n.add_tournament_name, + onChanged: (_) => notifier.onChange(), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + borderRadius: BorderRadius.circular(12), + borderType: AppTextFieldBorderType.outline, + borderColor: BorderColor( + focusColor: context.colorScheme.outline, + unFocusColor: context.colorScheme.outline, + ), + ), + const SizedBox(height: 16), + _selectTileView( + label: context.l10n.add_tournament_type, + title: state.selectedType.getString(context), + icon: Icons.keyboard_arrow_down_rounded, + onTap: () { + _selectTypeSheet( + context, + selectedType: state.selectedType, + onSelected: (type) => notifier.onSelectType(type), + ); + }, + ), + const SizedBox(height: 16), + _dateScheduleView(context, state), + const SizedBox(height: 16), + _selectTileView( + icon: Icons.keyboard_arrow_right_rounded, + label: context.l10n.add_tournament_team_selection, + title: context.l10n.add_tournament_team_count(state.teamIds.length), + onTap: () {}, + ), + const SizedBox(height: 16), + _selectTileView( + icon: Icons.keyboard_arrow_right_rounded, + label: context.l10n.add_tournament_matches_selection, + title: context.l10n.add_tournament_match_count(state.matchIds.length), + onTap: () {}, + ), ], ); } - Widget _topHeader(BuildContext context, AddTournamentState state) { + Widget _stickyButton( + BuildContext context, + AddTournamentState state, + ) { + return BottomStickyOverlay( + child: PrimaryButton( + context.l10n.common_create, + progress: state.loading, + enabled: state.enableButton, + onPressed: () => notifier.addTournament(), + ), + ); + } + + Widget _profileView(BuildContext context, AddTournamentState state) { return Padding( padding: const EdgeInsets.symmetric(vertical: 24.0), child: ProfileImageAvatar( size: 120, - isLoading: state.imageUploading && state.profileFilePath == null, + isLoading: state.imageUploading && state.filePath == null, shape: BoxShape.rectangle, - filePath: state.profileFilePath, - imageUrl: state.profileImageUrl, + filePath: state.filePath, + imageUrl: state.imageUrl, placeHolderImage: Assets.images.icTournaments, placeHolderColor: context.colorScheme.textPrimary, color: context.colorScheme.containerHigh, @@ -67,4 +165,125 @@ class _AddTournamentScreenState extends ConsumerState { ), ); } + + Widget _selectTileView({ + String? label, + IconData? icon, + required String title, + required Function() onTap, + }) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (label != null) ...[ + Text( + label, + style: AppTextStyle.body2.copyWith( + color: context.colorScheme.textDisabled, + ), + ), + const SizedBox(height: 12), + ], + OnTapScale( + onTap: onTap, + child: Material( + type: MaterialType.transparency, + child: ListTile( + dense: true, + contentPadding: const EdgeInsets.symmetric(horizontal: 12), + shape: RoundedRectangleBorder( + side: BorderSide(color: context.colorScheme.outline), + borderRadius: BorderRadius.circular(12), + ), + title: Text( + title, + style: AppTextStyle.body2.copyWith( + color: context.colorScheme.textPrimary, + ), + ), + trailing: Icon( + icon, + color: context.colorScheme.textDisabled, + ), + ), + ), + ), + ], + ); + } + + Widget _dateScheduleView( + BuildContext context, + AddTournamentState state, + ) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: AdaptiveOutlinedTile( + title: state.startDate.format(context, DateFormatType.date), + headerText: context.l10n.add_tournament_start_date, + placeholder: context.l10n.add_tournament_start_date, + onTap: () { + selectDate( + context, + initialDate: state.startDate, + onDateSelected: (date) => notifier.onStartDate(date), + ); + }), + ), + const SizedBox(width: 16), + Expanded( + child: AdaptiveOutlinedTile( + title: state.endDate.format(context, DateFormatType.date), + headerText: context.l10n.add_tournament_end_date, + placeholder: context.l10n.add_tournament_end_date, + onTap: () { + selectDate( + context, + initialDate: state.endDate, + onDateSelected: (date) => notifier.onEndDate(date), + ); + }), + ), + ], + ), + ], + ); + } + + void _selectTypeSheet( + BuildContext context, { + required TournamentType selectedType, + required Function(TournamentType) onSelected, + }) { + showActionBottomSheet( + context: context, + heightFactor: 0.8, + items: TournamentType.values + .map( + (type) => BottomSheetAction( + title: type.getString(context), + subTitle: type.getDescriptionString(context), + enabled: selectedType != type, + child: (selectedType == type) + ? SvgPicture.asset( + Assets.images.icCheck, + colorFilter: ColorFilter.mode( + context.colorScheme.primary, + BlendMode.srcATop, + ), + ) + : null, + onTap: () { + context.pop(); + onSelected.call(type); + }, + ), + ) + .toList(), + ); + } } diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index ca7e7370..618629b1 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -1,6 +1,11 @@ import 'dart:io'; +import 'package:data/api/tournament/tournament_model.dart'; import 'package:data/errors/app_error.dart'; +import 'package:data/service/file_upload/file_upload_service.dart'; +import 'package:data/service/tournament/tournament_service.dart'; +import 'package:data/storage/app_preferences.dart'; +import 'package:data/utils/constant/firebase_storage_constant.dart'; import 'package:flutter/material.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import 'package:hooks_riverpod/hooks_riverpod.dart'; @@ -9,18 +14,42 @@ import 'package:khelo/domain/extensions/file_extension.dart'; part 'add_tournament_view_model.freezed.dart'; final addTournamentStateProvider = StateNotifierProvider.autoDispose< - AddTournamentViewNotifier, - AddTournamentState>((ref) => AddTournamentViewNotifier()); + AddTournamentViewNotifier, AddTournamentState>((ref) { + final notifier = AddTournamentViewNotifier( + ref.read(tournamentServiceProvider), + ref.read(fileUploadServiceProvider), + ref.read(currentUserPod)?.id, + ); + ref.listen(currentUserPod, (previous, next) { + notifier.setUserId(next?.id); + }); + return notifier; +}); class AddTournamentViewNotifier extends StateNotifier { - AddTournamentViewNotifier() : super(const AddTournamentState()); + final FileUploadService _fileUploadService; + final TournamentService _tournamentService; + + AddTournamentViewNotifier( + this._tournamentService, + this._fileUploadService, + String? userId, + ) : super(AddTournamentState( + startDate: DateTime.now(), + endDate: DateTime.now().add(const Duration(days: 1)), + nameController: TextEditingController(), + currentUserId: userId, + )); + + void setUserId(String? userId) { + state = state.copyWith(currentUserId: userId); + } Future onImageChange(String imagePath) async { try { state = state.copyWith(imageUploading: true, actionError: null); if (await File(imagePath).isFileUnderMaxSize()) { - state = - state.copyWith(profileFilePath: imagePath, imageUploading: false); + state = state.copyWith(filePath: imagePath, imageUploading: false); } else { state = state.copyWith( imageUploading: false, @@ -28,7 +57,105 @@ class AddTournamentViewNotifier extends StateNotifier { } } catch (e) { state = state.copyWith(imageUploading: false, actionError: e); - debugPrint("EditProfileViewNotifier: error while image upload -> $e"); + debugPrint("AddTournamentViewNotifier: error while image upload -> $e"); + } + } + + void onChange() { + final name = state.nameController.text.trim(); + // final isValidTeams = state.teamIds.isNotEmpty && + // validateTypeWithTeam(type: state.selectedType, teamIds: state.teamIds); + + state = state.copyWith(enableButton: name.isNotEmpty); + } + + void onSelectType(TournamentType type) { + state = state.copyWith(selectedType: type); + } + + void onStartDate(DateTime startDate) { + state = state.copyWith(startDate: startDate); + } + + void onEndDate(DateTime endDate) { + state = state.copyWith(endDate: endDate); + } + + void onSelectTeams(List teamIds) { + state = state.copyWith(teamIds: teamIds); + onChange(); + } + + void onSelectMatches(List matches) { + state = state.copyWith(teamIds: matches); + } + + void addTournament() async { + try { + state = state.copyWith(loading: true); + final tournamentId = _tournamentService.generateTeamId; + final name = state.nameController.text.trim(); + + if (state.filePath != null && state.currentUserId != null) { + final imageUrl = await _fileUploadService.uploadProfileImage( + filePath: state.filePath ?? '', + uploadPath: StorageConst.tournamentProfileUploadPath( + userId: state.currentUserId ?? 'INVALID ID', + tournamentId: tournamentId), + ); + state = state.copyWith(imageUrl: imageUrl); + } + + final tournament = TournamentModel( + id: tournamentId, + name: name, + type: state.selectedType, + start_date: state.startDate, + end_date: state.endDate, + created_at: DateTime.now(), + created_by: state.currentUserId ?? 'INVALID ID', + members: [ + TournamentMember( + id: state.currentUserId ?? 'INVALID ID', + role: TournamentMemberRole.organizer, + ), + ], + profile_img_url: state.imageUrl, + team_ids: state.teamIds, + match_ids: state.matchIds, + ); + + await _tournamentService.updateTournament(tournament); + + state = state.copyWith(pop: true, loading: false, error: null); + } catch (error) { + state = state.copyWith(error: error); + debugPrint( + "AddTournamentViewNotifier: error while adding tournament -> $error"); + } + } + + bool validateTypeWithTeam({ + required TournamentType type, + required List teamIds, + }) { + switch (type) { + case TournamentType.knockOut: + case TournamentType.superOver: + case TournamentType.bestOf: + case TournamentType.gully: + case TournamentType.mixed: + return teamIds.length >= 2; + + case TournamentType.miniRobin: + return teamIds.length >= 3; + + case TournamentType.boxLeague: + case TournamentType.doubleOut: + return teamIds.length >= 4; + + case TournamentType.other: + return true; } } } @@ -38,9 +165,18 @@ class AddTournamentState with _$AddTournamentState { const factory AddTournamentState({ Object? error, Object? actionError, - @Default(false) bool imageUploading, + String? filePath, + String? currentUserId, + required DateTime endDate, + required DateTime startDate, + @Default(false) bool pop, @Default(false) bool loading, - String? profileFilePath, - @Default(null) String? profileImageUrl, + @Default(false) bool enableButton, + @Default([]) List teamIds, + @Default([]) List matchIds, + @Default(false) bool imageUploading, + @Default(null) String? imageUrl, + required TextEditingController nameController, + @Default(TournamentType.knockOut) TournamentType selectedType, }) = _AddTournamentState; } diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart index ee310a22..9bd60ebc 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -18,10 +18,20 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$AddTournamentState { Object? get error => throw _privateConstructorUsedError; Object? get actionError => throw _privateConstructorUsedError; - bool get imageUploading => throw _privateConstructorUsedError; + String? get filePath => throw _privateConstructorUsedError; + String? get currentUserId => throw _privateConstructorUsedError; + DateTime get endDate => throw _privateConstructorUsedError; + DateTime get startDate => throw _privateConstructorUsedError; + bool get pop => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; - String? get profileFilePath => throw _privateConstructorUsedError; - String? get profileImageUrl => throw _privateConstructorUsedError; + bool get enableButton => throw _privateConstructorUsedError; + List get teamIds => throw _privateConstructorUsedError; + List get matchIds => throw _privateConstructorUsedError; + bool get imageUploading => throw _privateConstructorUsedError; + String? get imageUrl => throw _privateConstructorUsedError; + TextEditingController get nameController => + throw _privateConstructorUsedError; + TournamentType get selectedType => throw _privateConstructorUsedError; /// Create a copy of AddTournamentState /// with the given fields replaced by the non-null parameter values. @@ -39,10 +49,19 @@ abstract class $AddTournamentStateCopyWith<$Res> { $Res call( {Object? error, Object? actionError, - bool imageUploading, + String? filePath, + String? currentUserId, + DateTime endDate, + DateTime startDate, + bool pop, bool loading, - String? profileFilePath, - String? profileImageUrl}); + bool enableButton, + List teamIds, + List matchIds, + bool imageUploading, + String? imageUrl, + TextEditingController nameController, + TournamentType selectedType}); } /// @nodoc @@ -62,30 +81,75 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> $Res call({ Object? error = freezed, Object? actionError = freezed, - Object? imageUploading = null, + Object? filePath = freezed, + Object? currentUserId = freezed, + Object? endDate = null, + Object? startDate = null, + Object? pop = null, Object? loading = null, - Object? profileFilePath = freezed, - Object? profileImageUrl = freezed, + Object? enableButton = null, + Object? teamIds = null, + Object? matchIds = null, + Object? imageUploading = null, + Object? imageUrl = freezed, + Object? nameController = null, + Object? selectedType = null, }) { return _then(_value.copyWith( error: freezed == error ? _value.error : error, actionError: freezed == actionError ? _value.actionError : actionError, - imageUploading: null == imageUploading - ? _value.imageUploading - : imageUploading // ignore: cast_nullable_to_non_nullable + filePath: freezed == filePath + ? _value.filePath + : filePath // ignore: cast_nullable_to_non_nullable + as String?, + currentUserId: freezed == currentUserId + ? _value.currentUserId + : currentUserId // ignore: cast_nullable_to_non_nullable + as String?, + endDate: null == endDate + ? _value.endDate + : endDate // ignore: cast_nullable_to_non_nullable + as DateTime, + startDate: null == startDate + ? _value.startDate + : startDate // ignore: cast_nullable_to_non_nullable + as DateTime, + pop: null == pop + ? _value.pop + : pop // ignore: cast_nullable_to_non_nullable as bool, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, - profileFilePath: freezed == profileFilePath - ? _value.profileFilePath - : profileFilePath // ignore: cast_nullable_to_non_nullable - as String?, - profileImageUrl: freezed == profileImageUrl - ? _value.profileImageUrl - : profileImageUrl // ignore: cast_nullable_to_non_nullable + enableButton: null == enableButton + ? _value.enableButton + : enableButton // ignore: cast_nullable_to_non_nullable + as bool, + teamIds: null == teamIds + ? _value.teamIds + : teamIds // ignore: cast_nullable_to_non_nullable + as List, + matchIds: null == matchIds + ? _value.matchIds + : matchIds // ignore: cast_nullable_to_non_nullable + as List, + imageUploading: null == imageUploading + ? _value.imageUploading + : imageUploading // ignore: cast_nullable_to_non_nullable + as bool, + imageUrl: freezed == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable as String?, + nameController: null == nameController + ? _value.nameController + : nameController // ignore: cast_nullable_to_non_nullable + as TextEditingController, + selectedType: null == selectedType + ? _value.selectedType + : selectedType // ignore: cast_nullable_to_non_nullable + as TournamentType, ) as $Val); } } @@ -101,10 +165,19 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> $Res call( {Object? error, Object? actionError, - bool imageUploading, + String? filePath, + String? currentUserId, + DateTime endDate, + DateTime startDate, + bool pop, bool loading, - String? profileFilePath, - String? profileImageUrl}); + bool enableButton, + List teamIds, + List matchIds, + bool imageUploading, + String? imageUrl, + TextEditingController nameController, + TournamentType selectedType}); } /// @nodoc @@ -122,30 +195,75 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> $Res call({ Object? error = freezed, Object? actionError = freezed, - Object? imageUploading = null, + Object? filePath = freezed, + Object? currentUserId = freezed, + Object? endDate = null, + Object? startDate = null, + Object? pop = null, Object? loading = null, - Object? profileFilePath = freezed, - Object? profileImageUrl = freezed, + Object? enableButton = null, + Object? teamIds = null, + Object? matchIds = null, + Object? imageUploading = null, + Object? imageUrl = freezed, + Object? nameController = null, + Object? selectedType = null, }) { return _then(_$AddTournamentStateImpl( error: freezed == error ? _value.error : error, actionError: freezed == actionError ? _value.actionError : actionError, - imageUploading: null == imageUploading - ? _value.imageUploading - : imageUploading // ignore: cast_nullable_to_non_nullable + filePath: freezed == filePath + ? _value.filePath + : filePath // ignore: cast_nullable_to_non_nullable + as String?, + currentUserId: freezed == currentUserId + ? _value.currentUserId + : currentUserId // ignore: cast_nullable_to_non_nullable + as String?, + endDate: null == endDate + ? _value.endDate + : endDate // ignore: cast_nullable_to_non_nullable + as DateTime, + startDate: null == startDate + ? _value.startDate + : startDate // ignore: cast_nullable_to_non_nullable + as DateTime, + pop: null == pop + ? _value.pop + : pop // ignore: cast_nullable_to_non_nullable as bool, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, - profileFilePath: freezed == profileFilePath - ? _value.profileFilePath - : profileFilePath // ignore: cast_nullable_to_non_nullable - as String?, - profileImageUrl: freezed == profileImageUrl - ? _value.profileImageUrl - : profileImageUrl // ignore: cast_nullable_to_non_nullable + enableButton: null == enableButton + ? _value.enableButton + : enableButton // ignore: cast_nullable_to_non_nullable + as bool, + teamIds: null == teamIds + ? _value._teamIds + : teamIds // ignore: cast_nullable_to_non_nullable + as List, + matchIds: null == matchIds + ? _value._matchIds + : matchIds // ignore: cast_nullable_to_non_nullable + as List, + imageUploading: null == imageUploading + ? _value.imageUploading + : imageUploading // ignore: cast_nullable_to_non_nullable + as bool, + imageUrl: freezed == imageUrl + ? _value.imageUrl + : imageUrl // ignore: cast_nullable_to_non_nullable as String?, + nameController: null == nameController + ? _value.nameController + : nameController // ignore: cast_nullable_to_non_nullable + as TextEditingController, + selectedType: null == selectedType + ? _value.selectedType + : selectedType // ignore: cast_nullable_to_non_nullable + as TournamentType, )); } } @@ -156,30 +274,76 @@ class _$AddTournamentStateImpl implements _AddTournamentState { const _$AddTournamentStateImpl( {this.error, this.actionError, - this.imageUploading = false, + this.filePath, + this.currentUserId, + required this.endDate, + required this.startDate, + this.pop = false, this.loading = false, - this.profileFilePath, - this.profileImageUrl = null}); + this.enableButton = false, + final List teamIds = const [], + final List matchIds = const [], + this.imageUploading = false, + this.imageUrl = null, + required this.nameController, + this.selectedType = TournamentType.knockOut}) + : _teamIds = teamIds, + _matchIds = matchIds; @override final Object? error; @override final Object? actionError; @override + final String? filePath; + @override + final String? currentUserId; + @override + final DateTime endDate; + @override + final DateTime startDate; + @override @JsonKey() - final bool imageUploading; + final bool pop; @override @JsonKey() final bool loading; @override - final String? profileFilePath; + @JsonKey() + final bool enableButton; + final List _teamIds; + @override + @JsonKey() + List get teamIds { + if (_teamIds is EqualUnmodifiableListView) return _teamIds; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_teamIds); + } + + final List _matchIds; + @override + @JsonKey() + List get matchIds { + if (_matchIds is EqualUnmodifiableListView) return _matchIds; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_matchIds); + } + + @override + @JsonKey() + final bool imageUploading; + @override + @JsonKey() + final String? imageUrl; + @override + final TextEditingController nameController; @override @JsonKey() - final String? profileImageUrl; + final TournamentType selectedType; @override String toString() { - return 'AddTournamentState(error: $error, actionError: $actionError, imageUploading: $imageUploading, loading: $loading, profileFilePath: $profileFilePath, profileImageUrl: $profileImageUrl)'; + return 'AddTournamentState(error: $error, actionError: $actionError, filePath: $filePath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, teamIds: $teamIds, matchIds: $matchIds, imageUploading: $imageUploading, imageUrl: $imageUrl, nameController: $nameController, selectedType: $selectedType)'; } @override @@ -190,13 +354,27 @@ class _$AddTournamentStateImpl implements _AddTournamentState { const DeepCollectionEquality().equals(other.error, error) && const DeepCollectionEquality() .equals(other.actionError, actionError) && + (identical(other.filePath, filePath) || + other.filePath == filePath) && + (identical(other.currentUserId, currentUserId) || + other.currentUserId == currentUserId) && + (identical(other.endDate, endDate) || other.endDate == endDate) && + (identical(other.startDate, startDate) || + other.startDate == startDate) && + (identical(other.pop, pop) || other.pop == pop) && + (identical(other.loading, loading) || other.loading == loading) && + (identical(other.enableButton, enableButton) || + other.enableButton == enableButton) && + const DeepCollectionEquality().equals(other._teamIds, _teamIds) && + const DeepCollectionEquality().equals(other._matchIds, _matchIds) && (identical(other.imageUploading, imageUploading) || other.imageUploading == imageUploading) && - (identical(other.loading, loading) || other.loading == loading) && - (identical(other.profileFilePath, profileFilePath) || - other.profileFilePath == profileFilePath) && - (identical(other.profileImageUrl, profileImageUrl) || - other.profileImageUrl == profileImageUrl)); + (identical(other.imageUrl, imageUrl) || + other.imageUrl == imageUrl) && + (identical(other.nameController, nameController) || + other.nameController == nameController) && + (identical(other.selectedType, selectedType) || + other.selectedType == selectedType)); } @override @@ -204,10 +382,19 @@ class _$AddTournamentStateImpl implements _AddTournamentState { runtimeType, const DeepCollectionEquality().hash(error), const DeepCollectionEquality().hash(actionError), - imageUploading, + filePath, + currentUserId, + endDate, + startDate, + pop, loading, - profileFilePath, - profileImageUrl); + enableButton, + const DeepCollectionEquality().hash(_teamIds), + const DeepCollectionEquality().hash(_matchIds), + imageUploading, + imageUrl, + nameController, + selectedType); /// Create a copy of AddTournamentState /// with the given fields replaced by the non-null parameter values. @@ -223,23 +410,50 @@ abstract class _AddTournamentState implements AddTournamentState { const factory _AddTournamentState( {final Object? error, final Object? actionError, - final bool imageUploading, + final String? filePath, + final String? currentUserId, + required final DateTime endDate, + required final DateTime startDate, + final bool pop, final bool loading, - final String? profileFilePath, - final String? profileImageUrl}) = _$AddTournamentStateImpl; + final bool enableButton, + final List teamIds, + final List matchIds, + final bool imageUploading, + final String? imageUrl, + required final TextEditingController nameController, + final TournamentType selectedType}) = _$AddTournamentStateImpl; @override Object? get error; @override Object? get actionError; @override - bool get imageUploading; + String? get filePath; + @override + String? get currentUserId; + @override + DateTime get endDate; + @override + DateTime get startDate; + @override + bool get pop; @override bool get loading; @override - String? get profileFilePath; + bool get enableButton; + @override + List get teamIds; + @override + List get matchIds; + @override + bool get imageUploading; + @override + String? get imageUrl; + @override + TextEditingController get nameController; @override - String? get profileImageUrl; + TournamentType get selectedType; /// Create a copy of AddTournamentState /// with the given fields replaced by the non-null parameter values. diff --git a/style/lib/pickers/date_and_time_picker.dart b/style/lib/pickers/date_and_time_picker.dart new file mode 100644 index 00000000..549cbc0e --- /dev/null +++ b/style/lib/pickers/date_and_time_picker.dart @@ -0,0 +1,71 @@ +import 'package:flutter/material.dart'; +import 'package:style/extensions/context_extensions.dart'; + +import '../theme/colors.dart'; + +Future selectDate( + BuildContext context, { + String? helpText, + required DateTime initialDate, + required Function(DateTime) onDateSelected, +}) async { + showDatePicker( + context: context, + helpText: helpText, + initialDate: initialDate, + firstDate: DateTime(1965), + lastDate: DateTime(2101), + builder: (context, child) { + return Theme( + data: context.brightness == Brightness.dark + ? materialThemeDataDark + : materialThemeDataLight, + child: child!, + ); + }, + ).then((selectedDate) { + if (selectedDate != null) { + DateTime selectedDateTime = DateTime( + selectedDate.year, + selectedDate.month, + selectedDate.day, + initialDate.hour, + initialDate.minute, + ); + onDateSelected.call(selectedDateTime); + } + }); +} + +Future selectTime( + BuildContext context, { + required DateTime initialTime, + required Function(DateTime) onTimeSelected, +}) async { + showTimePicker( + context: context, + initialTime: TimeOfDay( + hour: initialTime.hour, + minute: initialTime.minute, + ), + builder: (context, child) { + return Theme( + data: context.brightness == Brightness.dark + ? materialThemeDataDark + : materialThemeDataLight, + child: child!, + ); + }, + ).then((selectedTime) { + if (selectedTime != null) { + DateTime selectedDateTime = DateTime( + initialTime.year, + initialTime.month, + initialTime.day, + selectedTime.hour, + selectedTime.minute, + ); + onTimeSelected.call(selectedDateTime); + } + }); +} From 281ac91b747ed67679061b5fddd11145c26af6f9 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 27 Sep 2024 17:46:32 +0530 Subject: [PATCH 03/33] minor changes --- khelo/lib/ui/flow/my_game/my_game_tab_screen.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart index f4e9ff73..fcd32701 100644 --- a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart +++ b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart @@ -91,7 +91,7 @@ class _MyGameTabScreenState extends ConsumerState padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ - const SizedBox(width: 16), + const SizedBox(width: 8), TabButton( context.l10n.common_matches_title, selected: _selectedTab == 0, @@ -139,6 +139,7 @@ class _MyGameTabScreenState extends ConsumerState Icons.add, color: context.colorScheme.textPrimary, )), + const SizedBox(width: 4), ], ), ); From cfa3d1afd53b011f488aea70d8de52235bffcf59 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Mon, 30 Sep 2024 12:32:32 +0530 Subject: [PATCH 04/33] minor changes --- data/lib/api/tournament/tournament_model.dart | 2 +- .../tournament/tournament_model.freezed.dart | 26 ++++---- .../api/tournament/tournament_model.g.dart | 7 ++- .../tournament/tournament_service.dart | 4 +- khelo/assets/locales/app_en.arb | 14 ----- .../ui/flow/my_game/my_game_tab_screen.dart | 48 +++++++------- .../tournament/add/add_tournament_screen.dart | 14 ----- .../add/add_tournament_view_model.dart | 47 ++------------ .../add_tournament_view_model.freezed.dart | 62 +------------------ style/lib/button/action_button.dart | 7 ++- 10 files changed, 59 insertions(+), 172 deletions(-) diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index 4e2b4f90..b753644e 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -23,7 +23,7 @@ class TournamentModel with _$TournamentModel { required String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() required DateTime start_date, - @TimeStampJsonConverter() required DateTime end_date, + @TimeStampJsonConverter() DateTime? end_date, @Default([]) List team_ids, @Default([]) List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index a9e593d1..e9e58ef1 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -31,7 +31,7 @@ mixin _$TournamentModel { @TimeStampJsonConverter() DateTime get start_date => throw _privateConstructorUsedError; @TimeStampJsonConverter() - DateTime get end_date => throw _privateConstructorUsedError; + DateTime? get end_date => throw _privateConstructorUsedError; List get team_ids => throw _privateConstructorUsedError; List get match_ids => throw _privateConstructorUsedError; @JsonKey(includeFromJson: false, includeToJson: false) @@ -60,7 +60,7 @@ abstract class $TournamentModelCopyWith<$Res> { String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() DateTime start_date, - @TimeStampJsonConverter() DateTime end_date, + @TimeStampJsonConverter() DateTime? end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -90,7 +90,7 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> Object? created_by = null, Object? created_at = freezed, Object? start_date = null, - Object? end_date = null, + Object? end_date = freezed, Object? team_ids = null, Object? match_ids = null, Object? teams = null, @@ -129,10 +129,10 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable as DateTime, - end_date: null == end_date + end_date: freezed == end_date ? _value.end_date : end_date // ignore: cast_nullable_to_non_nullable - as DateTime, + as DateTime?, team_ids: null == team_ids ? _value.team_ids : team_ids // ignore: cast_nullable_to_non_nullable @@ -170,7 +170,7 @@ abstract class _$$TournamentModelImplCopyWith<$Res> String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() DateTime start_date, - @TimeStampJsonConverter() DateTime end_date, + @TimeStampJsonConverter() DateTime? end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -198,7 +198,7 @@ class __$$TournamentModelImplCopyWithImpl<$Res> Object? created_by = null, Object? created_at = freezed, Object? start_date = null, - Object? end_date = null, + Object? end_date = freezed, Object? team_ids = null, Object? match_ids = null, Object? teams = null, @@ -237,10 +237,10 @@ class __$$TournamentModelImplCopyWithImpl<$Res> ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable as DateTime, - end_date: null == end_date + end_date: freezed == end_date ? _value.end_date : end_date // ignore: cast_nullable_to_non_nullable - as DateTime, + as DateTime?, team_ids: null == team_ids ? _value._team_ids : team_ids // ignore: cast_nullable_to_non_nullable @@ -274,7 +274,7 @@ class _$TournamentModelImpl implements _TournamentModel { required this.created_by, @TimeStampJsonConverter() this.created_at, @TimeStampJsonConverter() required this.start_date, - @TimeStampJsonConverter() required this.end_date, + @TimeStampJsonConverter() this.end_date, final List team_ids = const [], final List match_ids = const [], @JsonKey(includeFromJson: false, includeToJson: false) @@ -317,7 +317,7 @@ class _$TournamentModelImpl implements _TournamentModel { final DateTime start_date; @override @TimeStampJsonConverter() - final DateTime end_date; + final DateTime? end_date; final List _team_ids; @override @JsonKey() @@ -428,7 +428,7 @@ abstract class _TournamentModel implements TournamentModel { required final String created_by, @TimeStampJsonConverter() final DateTime? created_at, @TimeStampJsonConverter() required final DateTime start_date, - @TimeStampJsonConverter() required final DateTime end_date, + @TimeStampJsonConverter() final DateTime? end_date, final List team_ids, final List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -459,7 +459,7 @@ abstract class _TournamentModel implements TournamentModel { DateTime get start_date; @override @TimeStampJsonConverter() - DateTime get end_date; + DateTime? get end_date; @override List get team_ids; @override diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index 76d2270c..2ed63445 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -22,8 +22,8 @@ _$TournamentModelImpl _$$TournamentModelImplFromJson(Map json) => json['created_at'], const TimeStampJsonConverter().fromJson), start_date: const TimeStampJsonConverter().fromJson(json['start_date'] as Object), - end_date: - const TimeStampJsonConverter().fromJson(json['end_date'] as Object), + end_date: _$JsonConverterFromJson( + json['end_date'], const TimeStampJsonConverter().fromJson), team_ids: (json['team_ids'] as List?) ?.map((e) => e as String) .toList() ?? @@ -46,7 +46,8 @@ Map _$$TournamentModelImplToJson( 'created_at': _$JsonConverterToJson( instance.created_at, const TimeStampJsonConverter().toJson), 'start_date': const TimeStampJsonConverter().toJson(instance.start_date), - 'end_date': const TimeStampJsonConverter().toJson(instance.end_date), + 'end_date': _$JsonConverterToJson( + instance.end_date, const TimeStampJsonConverter().toJson), 'team_ids': instance.team_ids, 'match_ids': instance.match_ids, }; diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index 54b5bbca..a6e2e66b 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -20,9 +20,9 @@ class TournamentService { toFirestore: (TournamentModel tournament, _) => tournament.toJson(), ); - String get generateTeamId => _tournamentCollection.doc().id; + String get generateTournamentId => _tournamentCollection.doc().id; - Future updateTournament(TournamentModel tournament) async { + Future createTournament(TournamentModel tournament) async { try { await _tournamentCollection .doc(tournament.id) diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 4f7095f6..ac5643f2 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -149,20 +149,6 @@ "add_tournament_type": "Tournament Type", "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", - "add_tournament_team_selection": "Team selection", - "add_tournament_matches_selection": "Matches selection", - "add_tournament_team_count": "{count, plural, =0{Select Team} =1{{count} Team} other{{count} Teams}}", - "@add_tournament_team_count": { - "placeholders": { - "count": {} - } - }, - "add_tournament_match_count": "{count, plural, =0{Select Match} =1{{count} Match} other{{count} Matches}}", - "@add_tournament_match_count": { - "placeholders": { - "count": {} - } - }, "@_TOURNAMENT_TYPE":{ diff --git a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart index fcd32701..746a6f86 100644 --- a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart +++ b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart @@ -91,7 +91,7 @@ class _MyGameTabScreenState extends ConsumerState padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ - const SizedBox(width: 8), + const SizedBox(width: 16), TabButton( context.l10n.common_matches_title, selected: _selectedTab == 0, @@ -118,28 +118,34 @@ class _MyGameTabScreenState extends ConsumerState const Spacer(), if (_selectedTab == 1 && ref.watch(teamListViewStateProvider).teams.isNotEmpty) ...[ - actionButton(context, - onPressed: () => ref - .read(teamListViewStateProvider.notifier) - .onFilterButtonTap(), - icon: Icon( - CupertinoIcons.slider_horizontal_3, - color: context.colorScheme.textPrimary, - )), - ], - actionButton(context, onPressed: () { - final actions = [ - AppRoute.addMatch(), - AppRoute.addTeam(), - AppRoute.addTournament(), - ]; - actions[_selectedTab].push(context); - }, + actionButton( + context, + onPressed: () => ref + .read(teamListViewStateProvider.notifier) + .onFilterButtonTap(), icon: Icon( - Icons.add, + CupertinoIcons.slider_horizontal_3, color: context.colorScheme.textPrimary, - )), - const SizedBox(width: 4), + ), + shrinkWrap: true, + ), + ], + actionButton( + context, + onPressed: () { + final actions = [ + AppRoute.addMatch(), + AppRoute.addTeam(), + AppRoute.addTournament(), + ]; + actions[_selectedTab].push(context); + }, + icon: Icon( + Icons.add, + color: context.colorScheme.textPrimary, + ), + shrinkWrap: true, + ), ], ), ); diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index 98cd621f..5646a766 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -111,20 +111,6 @@ class _AddTournamentScreenState extends ConsumerState { ), const SizedBox(height: 16), _dateScheduleView(context, state), - const SizedBox(height: 16), - _selectTileView( - icon: Icons.keyboard_arrow_right_rounded, - label: context.l10n.add_tournament_team_selection, - title: context.l10n.add_tournament_team_count(state.teamIds.length), - onTap: () {}, - ), - const SizedBox(height: 16), - _selectTileView( - icon: Icons.keyboard_arrow_right_rounded, - label: context.l10n.add_tournament_matches_selection, - title: context.l10n.add_tournament_match_count(state.matchIds.length), - onTap: () {}, - ), ], ); } diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index 618629b1..7dac026e 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -63,8 +63,6 @@ class AddTournamentViewNotifier extends StateNotifier { void onChange() { final name = state.nameController.text.trim(); - // final isValidTeams = state.teamIds.isNotEmpty && - // validateTypeWithTeam(type: state.selectedType, teamIds: state.teamIds); state = state.copyWith(enableButton: name.isNotEmpty); } @@ -81,19 +79,12 @@ class AddTournamentViewNotifier extends StateNotifier { state = state.copyWith(endDate: endDate); } - void onSelectTeams(List teamIds) { - state = state.copyWith(teamIds: teamIds); - onChange(); - } - - void onSelectMatches(List matches) { - state = state.copyWith(teamIds: matches); - } - void addTournament() async { try { + if (state.currentUserId == null) return; + state = state.copyWith(loading: true); - final tournamentId = _tournamentService.generateTeamId; + final tournamentId = _tournamentService.generateTournamentId; final name = state.nameController.text.trim(); if (state.filePath != null && state.currentUserId != null) { @@ -121,43 +112,17 @@ class AddTournamentViewNotifier extends StateNotifier { ), ], profile_img_url: state.imageUrl, - team_ids: state.teamIds, - match_ids: state.matchIds, ); - await _tournamentService.updateTournament(tournament); + await _tournamentService.createTournament(tournament); state = state.copyWith(pop: true, loading: false, error: null); } catch (error) { - state = state.copyWith(error: error); + state = state.copyWith(loading: false, error: error); debugPrint( "AddTournamentViewNotifier: error while adding tournament -> $error"); } } - - bool validateTypeWithTeam({ - required TournamentType type, - required List teamIds, - }) { - switch (type) { - case TournamentType.knockOut: - case TournamentType.superOver: - case TournamentType.bestOf: - case TournamentType.gully: - case TournamentType.mixed: - return teamIds.length >= 2; - - case TournamentType.miniRobin: - return teamIds.length >= 3; - - case TournamentType.boxLeague: - case TournamentType.doubleOut: - return teamIds.length >= 4; - - case TournamentType.other: - return true; - } - } } @freezed @@ -172,8 +137,6 @@ class AddTournamentState with _$AddTournamentState { @Default(false) bool pop, @Default(false) bool loading, @Default(false) bool enableButton, - @Default([]) List teamIds, - @Default([]) List matchIds, @Default(false) bool imageUploading, @Default(null) String? imageUrl, required TextEditingController nameController, diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart index 9bd60ebc..8be804bc 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -25,8 +25,6 @@ mixin _$AddTournamentState { bool get pop => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; bool get enableButton => throw _privateConstructorUsedError; - List get teamIds => throw _privateConstructorUsedError; - List get matchIds => throw _privateConstructorUsedError; bool get imageUploading => throw _privateConstructorUsedError; String? get imageUrl => throw _privateConstructorUsedError; TextEditingController get nameController => @@ -56,8 +54,6 @@ abstract class $AddTournamentStateCopyWith<$Res> { bool pop, bool loading, bool enableButton, - List teamIds, - List matchIds, bool imageUploading, String? imageUrl, TextEditingController nameController, @@ -88,8 +84,6 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> Object? pop = null, Object? loading = null, Object? enableButton = null, - Object? teamIds = null, - Object? matchIds = null, Object? imageUploading = null, Object? imageUrl = freezed, Object? nameController = null, @@ -126,14 +120,6 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> ? _value.enableButton : enableButton // ignore: cast_nullable_to_non_nullable as bool, - teamIds: null == teamIds - ? _value.teamIds - : teamIds // ignore: cast_nullable_to_non_nullable - as List, - matchIds: null == matchIds - ? _value.matchIds - : matchIds // ignore: cast_nullable_to_non_nullable - as List, imageUploading: null == imageUploading ? _value.imageUploading : imageUploading // ignore: cast_nullable_to_non_nullable @@ -172,8 +158,6 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> bool pop, bool loading, bool enableButton, - List teamIds, - List matchIds, bool imageUploading, String? imageUrl, TextEditingController nameController, @@ -202,8 +186,6 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> Object? pop = null, Object? loading = null, Object? enableButton = null, - Object? teamIds = null, - Object? matchIds = null, Object? imageUploading = null, Object? imageUrl = freezed, Object? nameController = null, @@ -240,14 +222,6 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> ? _value.enableButton : enableButton // ignore: cast_nullable_to_non_nullable as bool, - teamIds: null == teamIds - ? _value._teamIds - : teamIds // ignore: cast_nullable_to_non_nullable - as List, - matchIds: null == matchIds - ? _value._matchIds - : matchIds // ignore: cast_nullable_to_non_nullable - as List, imageUploading: null == imageUploading ? _value.imageUploading : imageUploading // ignore: cast_nullable_to_non_nullable @@ -281,14 +255,10 @@ class _$AddTournamentStateImpl implements _AddTournamentState { this.pop = false, this.loading = false, this.enableButton = false, - final List teamIds = const [], - final List matchIds = const [], this.imageUploading = false, this.imageUrl = null, required this.nameController, - this.selectedType = TournamentType.knockOut}) - : _teamIds = teamIds, - _matchIds = matchIds; + this.selectedType = TournamentType.knockOut}); @override final Object? error; @@ -311,24 +281,6 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override @JsonKey() final bool enableButton; - final List _teamIds; - @override - @JsonKey() - List get teamIds { - if (_teamIds is EqualUnmodifiableListView) return _teamIds; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_teamIds); - } - - final List _matchIds; - @override - @JsonKey() - List get matchIds { - if (_matchIds is EqualUnmodifiableListView) return _matchIds; - // ignore: implicit_dynamic_type - return EqualUnmodifiableListView(_matchIds); - } - @override @JsonKey() final bool imageUploading; @@ -343,7 +295,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override String toString() { - return 'AddTournamentState(error: $error, actionError: $actionError, filePath: $filePath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, teamIds: $teamIds, matchIds: $matchIds, imageUploading: $imageUploading, imageUrl: $imageUrl, nameController: $nameController, selectedType: $selectedType)'; + return 'AddTournamentState(error: $error, actionError: $actionError, filePath: $filePath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, imageUploading: $imageUploading, imageUrl: $imageUrl, nameController: $nameController, selectedType: $selectedType)'; } @override @@ -365,8 +317,6 @@ class _$AddTournamentStateImpl implements _AddTournamentState { (identical(other.loading, loading) || other.loading == loading) && (identical(other.enableButton, enableButton) || other.enableButton == enableButton) && - const DeepCollectionEquality().equals(other._teamIds, _teamIds) && - const DeepCollectionEquality().equals(other._matchIds, _matchIds) && (identical(other.imageUploading, imageUploading) || other.imageUploading == imageUploading) && (identical(other.imageUrl, imageUrl) || @@ -389,8 +339,6 @@ class _$AddTournamentStateImpl implements _AddTournamentState { pop, loading, enableButton, - const DeepCollectionEquality().hash(_teamIds), - const DeepCollectionEquality().hash(_matchIds), imageUploading, imageUrl, nameController, @@ -417,8 +365,6 @@ abstract class _AddTournamentState implements AddTournamentState { final bool pop, final bool loading, final bool enableButton, - final List teamIds, - final List matchIds, final bool imageUploading, final String? imageUrl, required final TextEditingController nameController, @@ -443,10 +389,6 @@ abstract class _AddTournamentState implements AddTournamentState { @override bool get enableButton; @override - List get teamIds; - @override - List get matchIds; - @override bool get imageUploading; @override String? get imageUrl; diff --git a/style/lib/button/action_button.dart b/style/lib/button/action_button.dart index 76e15b89..854fac3d 100644 --- a/style/lib/button/action_button.dart +++ b/style/lib/button/action_button.dart @@ -8,6 +8,7 @@ Widget actionButton( void Function()? onPressed, required Widget icon, EdgeInsets padding = EdgeInsets.zero, + bool shrinkWrap = false, }) { if (Platform.isIOS) { return CupertinoButton( @@ -20,8 +21,10 @@ Widget actionButton( onPressed: onPressed, icon: icon, padding: padding, - style: const ButtonStyle( - tapTargetSize: MaterialTapTargetSize.shrinkWrap, + style: ButtonStyle( + tapTargetSize: shrinkWrap + ? MaterialTapTargetSize.shrinkWrap + : MaterialTapTargetSize.padded, ), ); } From 3e9f07732dc9aefb0f15d5ab6df3ff30497b40f3 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Wed, 2 Oct 2024 14:05:41 +0530 Subject: [PATCH 05/33] fix lint --- .../matches/add_match/add_match_screen.dart | 35 ------------------- 1 file changed, 35 deletions(-) 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 57cb66fb..95b250d3 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 @@ -351,41 +351,6 @@ class _AddMatchScreenState extends ConsumerState { ); } - void _showDeleteAlert( - BuildContext context, { - required Function() onDelete, - }) { - showAdaptiveDialog( - context: context, - builder: (context) { - return AlertDialog.adaptive( - title: Text(context.l10n.common_delete_title), - content: Text(context.l10n.alert_confirm_default_message( - context.l10n.common_delete_title.toLowerCase())), - actions: [ - TextButton( - onPressed: context.pop, - child: Text( - context.l10n.common_cancel_title, - style: TextStyle(color: context.colorScheme.textSecondary), - ), - ), - TextButton( - onPressed: () { - context.pop(); - onDelete(); - }, - child: Text( - context.l10n.common_delete_title, - style: TextStyle(color: context.colorScheme.alert), - ), - ), - ], - ); - }, - ); - } - void _observeActionError(BuildContext context, WidgetRef ref) { ref.listen(addMatchViewStateProvider.select((value) => value.actionError), (previous, next) { From 0ffb666b9b3bc0a82deab28ca41b703b0257bcbb Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Wed, 2 Oct 2024 16:47:44 +0530 Subject: [PATCH 06/33] Implement tournament list --- .../tournament/tournament_service.dart | 37 ++- .../utils/constant/firestore_constant.dart | 7 +- khelo/assets/images/ic_arrow_forward.svg | 3 + khelo/assets/locales/app_en.arb | 8 + .../lib/domain/formatter/date_formatter.dart | 14 + khelo/lib/gen/assets.gen.dart | 4 + .../tournament/tournament_list_screen.dart | 244 +++++++++++++++++- .../tournament_list_view_model.dart | 73 ++++++ .../tournament_list_view_model.freezed.dart | 201 +++++++++++++++ style/lib/extensions/date_extensions.dart | 2 + 10 files changed, 585 insertions(+), 8 deletions(-) create mode 100644 khelo/assets/images/ic_arrow_forward.svg create mode 100644 khelo/lib/ui/flow/tournament/tournament_list_view_model.dart create mode 100644 khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index a6e2e66b..f07791ae 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -3,16 +3,23 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../api/tournament/tournament_model.dart'; import '../../errors/app_error.dart'; +import '../../storage/app_preferences.dart'; import '../../utils/constant/firestore_constant.dart'; -final tournamentServiceProvider = Provider( - (ref) => TournamentService(FirebaseFirestore.instance), -); +final tournamentServiceProvider = Provider((ref) { + final service = TournamentService( + FirebaseFirestore.instance, + ref.read(currentUserPod)?.id, + ); + ref.listen(currentUserPod, (_, next) => service._currentUserId = next?.id); + return service; +}); class TournamentService { + String? _currentUserId; final FirebaseFirestore _firestore; - TournamentService(this._firestore); + TournamentService(this._firestore, this._currentUserId); CollectionReference get _tournamentCollection => _firestore.collection(FireStoreConst.tournamentCollection).withConverter( @@ -31,4 +38,26 @@ class TournamentService { throw AppError.fromError(error, stack); } } + + Stream> streamCurrentUserRelatedMatches() { + if (_currentUserId == null) { + return Stream.value([]); + } + + final filter = Filter.or( + Filter(FireStoreConst.createdBy, isEqualTo: _currentUserId), + Filter(FireStoreConst.members, arrayContains: _currentUserId), + ); + + return _tournamentCollection + .where(filter) + .snapshots() + .asyncMap((snapshot) async { + return await Future.wait( + snapshot.docs.map((mainDoc) async { + return mainDoc.data(); + }).toList(), + ); + }).handleError((error, stack) => throw AppError.fromError(error, stack)); + } } diff --git a/data/lib/utils/constant/firestore_constant.dart b/data/lib/utils/constant/firestore_constant.dart index cd2da68b..9539c7ad 100644 --- a/data/lib/utils/constant/firestore_constant.dart +++ b/data/lib/utils/constant/firestore_constant.dart @@ -51,7 +51,10 @@ class FireStoreConst { static const String notifications = "notifications"; static const String phone = "phone"; static const String name = "name"; -} + + // tournament filed const + static const String members = "members"; + } class DataConfig { static late DataConfig _instance; @@ -67,4 +70,4 @@ class DataConfig { DataConfig({ required this.apiBaseUrl, }); -} \ No newline at end of file +} diff --git a/khelo/assets/images/ic_arrow_forward.svg b/khelo/assets/images/ic_arrow_forward.svg new file mode 100644 index 00000000..d21845ad --- /dev/null +++ b/khelo/assets/images/ic_arrow_forward.svg @@ -0,0 +1,3 @@ + + + diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2870c08b..e02bf22c 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -150,6 +150,14 @@ "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", + "tournament_list_no_tournaments_created_title": "No tournaments created", + "tournament_list_empty_list_description": "Tap on the “ + ” icon to create a tournament", + "tournament_list_match_title": "{count, plural, =0{No matches} =1{{count} Match} other{{count} Matches}}", + "@tournament_list_match_title": { + "placeholders": { + "count": {} + } + }, "@_TOURNAMENT_TYPE":{ }, diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart index 174cc793..478c3ff2 100644 --- a/khelo/lib/domain/formatter/date_formatter.dart +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -6,6 +6,8 @@ enum DateFormatType { dateAndTime, date, time, + dayMonth, + monthYear, shortDate, shortDateTime, dayMonthYear @@ -33,6 +35,18 @@ extension DateFormatter on DateTime { .format(this); case DateFormatType.dayMonthYear: return DateFormat.yMMMd().format(this); + case DateFormatType.monthYear: + return DateFormat('MMMM yyyy').format(this); + case DateFormatType.dayMonth: + return DateFormat('dd MMM').format(this); } } + + static String formatDateRange( + BuildContext context, { + required DateTime startDate, + required DateTime endDate, + required DateFormatType formatType, + }) => + "${startDate.format(context, formatType)} - ${endDate.format(context, formatType)}"; } diff --git a/khelo/lib/gen/assets.gen.dart b/khelo/lib/gen/assets.gen.dart index 09957acd..51e19492 100644 --- a/khelo/lib/gen/assets.gen.dart +++ b/khelo/lib/gen/assets.gen.dart @@ -22,6 +22,9 @@ class $AssetsImagesGen { /// File path: assets/images/ic_arrow_down.svg String get icArrowDown => 'assets/images/ic_arrow_down.svg'; + /// File path: assets/images/ic_arrow_forward.svg + String get icArrowForward => 'assets/images/ic_arrow_forward.svg'; + /// File path: assets/images/ic_bat_selected.svg String get icBatSelected => 'assets/images/ic_bat_selected.svg'; @@ -151,6 +154,7 @@ class $AssetsImagesGen { bowler, icAboutUs, icArrowDown, + icArrowForward, icBatSelected, icBin, icCalendar, diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index a49e8b55..c0aeaf01 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -1,6 +1,21 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:data/api/tournament/tournament_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.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/formatter/date_formatter.dart'; +import 'package:khelo/ui/flow/tournament/tournament_list_view_model.dart'; +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 '../../../components/empty_screen.dart'; +import '../../../components/error_screen.dart'; +import '../../../gen/assets.gen.dart'; class TournamentListScreen extends ConsumerStatefulWidget { const TournamentListScreen({super.key}); @@ -10,7 +25,26 @@ class TournamentListScreen extends ConsumerStatefulWidget { _TournamentListScreenState(); } -class _TournamentListScreenState extends ConsumerState { +class _TournamentListScreenState extends ConsumerState + with WidgetsBindingObserver { + late TournamentListViewNotifier notifier; + + @override + void initState() { + WidgetsBinding.instance.addObserver(this); + super.initState(); + notifier = ref.read(tournamentListViewStateProvider.notifier); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.detached) { + // deallocate resources + notifier.dispose(); + WidgetsBinding.instance.removeObserver(this); + } + } + @override Widget build(BuildContext context) { return AppPage( @@ -21,6 +55,212 @@ class _TournamentListScreenState extends ConsumerState { } Widget _body(BuildContext context) { - return const Column(); + final state = ref.watch(tournamentListViewStateProvider); + if (state.loading) { + return const Center(child: AppProgressIndicator()); + } + if (state.error != null) { + return ErrorScreen( + error: state.error, + onRetryTap: notifier.onResume, + ); + } + if (state.groupTournaments.isEmpty) { + return EmptyScreen( + title: context.l10n.tournament_list_no_tournaments_created_title, + description: context.l10n.tournament_list_empty_list_description, + isShowButton: false, + ); + } + + return _content(context, state); } + + Widget _content(BuildContext context, TournamentListViewState state) { + return CustomScrollView( + slivers: [ + ..._tournaments(context, state), + SliverToBoxAdapter( + child: state.groupTournaments.isNotEmpty && state.loading + ? const Padding( + padding: EdgeInsets.all(16), + child: Center(child: AppProgressIndicator()), + ) + : const SizedBox(), + ), + SliverToBoxAdapter( + child: SizedBox(height: 16 + context.mediaQueryPadding.bottom), + ), + ], + ); + } + + List _tournaments( + BuildContext context, TournamentListViewState state) { + return List.generate(state.groupTournaments.length, (bookingIndex) { + final DateTime key = state.groupTournaments.keys.elementAt(bookingIndex); + final List tournaments = state.groupTournaments[key]!; + return SliverMainAxisGroup( + slivers: [ + SliverPersistentHeader( + pinned: true, + delegate: SliverAppbarDelegate( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Text( + key.format(context, DateFormatType.monthYear), + style: AppTextStyle.header3 + .copyWith(color: context.colorScheme.textPrimary), + textScaler: TextScaler.noScaling, + ), + ), + ), + ), + SliverList.builder( + itemBuilder: (BuildContext context, int index) { + final tournament = tournaments[index]; + return _tournamentItem(context, tournament); + }, + itemCount: tournaments.length, + ), + ], + ); + }); + } + + Widget _tournamentItem(BuildContext context, TournamentModel tournament) { + return OnTapScale( + onTap: () {}, + child: Container( + padding: const EdgeInsets.all(16), + margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + borderRadius: BorderRadius.circular(16)), + child: Row( + children: [ + _profileImage(context, tournament.profile_img_url), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + tournament.name, + style: AppTextStyle.header4.copyWith( + color: context.colorScheme.textPrimary, + ), + ), + const SizedBox(height: 4), + Text( + context.l10n.tournament_list_match_title( + tournament.match_ids.length), + style: AppTextStyle.subtitle2.copyWith( + color: context.colorScheme.textSecondary, + ), + ), + const SizedBox(height: 4), + _scheduleAndTypeView(context, tournament), + ], + ), + ), + const SizedBox(width: 16), + SvgPicture.asset(Assets.images.icArrowForward, + colorFilter: ColorFilter.mode( + context.colorScheme.textDisabled, + BlendMode.srcATop, + )) + ], + ), + ), + ); + } + + Widget _profileImage(BuildContext context, String? imageUrl) { + return Container( + height: 82, + width: 82, + decoration: BoxDecoration( + color: context.colorScheme.surface, + borderRadius: BorderRadius.circular(6), + image: (imageUrl != null) + ? DecorationImage( + image: CachedNetworkImageProvider(imageUrl), + fit: BoxFit.cover, + ) + : null), + child: imageUrl == null + ? Center( + child: SvgPicture.asset(Assets.images.icTournaments, + height: 32, + width: 32, + colorFilter: ColorFilter.mode( + context.colorScheme.textSecondary, + BlendMode.srcATop, + )), + ) + : null, + ); + } + + Widget _scheduleAndTypeView( + BuildContext context, + TournamentModel tournament, + ) { + return Text.rich(TextSpan( + text: (tournament.end_date != null) + ? DateFormatter.formatDateRange( + context, + startDate: tournament.start_date, + endDate: tournament.end_date!, + formatType: DateFormatType.dayMonth, + ) + : tournament.start_date.format(context, DateFormatType.dayMonth), + style: AppTextStyle.caption + .copyWith(color: context.colorScheme.textDisabled), + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Container( + height: 5, + width: 5, + margin: const EdgeInsets.symmetric(horizontal: 8), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: context.colorScheme.textSecondary, + ), + )), + TextSpan(text: tournament.type.getString(context)) + ])); + } +} + +class SliverAppbarDelegate extends SliverPersistentHeaderDelegate { + final Widget child; + + SliverAppbarDelegate({Key? key, required this.child}); + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { + return Container( + color: context.colorScheme.surface, + alignment: Alignment.centerLeft, + child: child, + ); + } + + @override + bool shouldRebuild(SliverAppbarDelegate oldDelegate) { + return child != oldDelegate.child; + } + + @override + double get maxExtent => 60; + + @override + double get minExtent => 60; } diff --git a/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart new file mode 100644 index 00000000..a23781e2 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart @@ -0,0 +1,73 @@ +import 'dart:async'; +import 'package:collection/collection.dart'; +import 'package:data/api/tournament/tournament_model.dart'; +import 'package:data/service/tournament/tournament_service.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; +import 'package:style/extensions/date_extensions.dart'; + +part 'tournament_list_view_model.freezed.dart'; + +final tournamentListViewStateProvider = + StateNotifierProvider( + (ref) => TournamentListViewNotifier(ref.read(tournamentServiceProvider)), +); + +class TournamentListViewNotifier + extends StateNotifier { + final TournamentService _tournamentService; + late StreamSubscription _tournamentStreamSubscription; + + TournamentListViewNotifier(this._tournamentService) + : super(const TournamentListViewState()) { + _loadTournamentList(); + } + + Future _loadTournamentList() async { + state = state.copyWith(loading: state.groupTournaments.isEmpty); + try { + _tournamentStreamSubscription = _tournamentService + .streamCurrentUserRelatedMatches() + .listen((tournaments) { + final groupTournaments = _groupTournaments(tournaments); + state = state.copyWith( + groupTournaments: groupTournaments, loading: false, error: null); + }, onError: (e) { + state = state.copyWith(loading: false, error: e); + debugPrint( + "TournamentListViewNotifier: error while loading tournament list -> $e"); + }); + } catch (e) { + state = state.copyWith(loading: false, error: e); + debugPrint( + "TournamentListViewNotifier: error while loading tournament list -> $e"); + } + } + + Map> _groupTournaments( + List tournaments) { + return groupBy( + tournaments, + (tournament) => tournament.start_date.startOfMonth, + ); + } + + _cancelStreamSubscription() { + _tournamentStreamSubscription.cancel(); + } + + onResume() { + _cancelStreamSubscription(); + _loadTournamentList(); + } +} + +@freezed +class TournamentListViewState with _$TournamentListViewState { + const factory TournamentListViewState({ + Object? error, + @Default(true) bool loading, + @Default({}) Map> groupTournaments, + }) = _TournamentListViewState; +} diff --git a/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart new file mode 100644 index 00000000..297993b6 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart @@ -0,0 +1,201 @@ +// 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 'tournament_list_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 _$TournamentListViewState { + Object? get error => throw _privateConstructorUsedError; + bool get loading => throw _privateConstructorUsedError; + Map> get groupTournaments => + throw _privateConstructorUsedError; + + /// Create a copy of TournamentListViewState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TournamentListViewStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TournamentListViewStateCopyWith<$Res> { + factory $TournamentListViewStateCopyWith(TournamentListViewState value, + $Res Function(TournamentListViewState) then) = + _$TournamentListViewStateCopyWithImpl<$Res, TournamentListViewState>; + @useResult + $Res call( + {Object? error, + bool loading, + Map> groupTournaments}); +} + +/// @nodoc +class _$TournamentListViewStateCopyWithImpl<$Res, + $Val extends TournamentListViewState> + implements $TournamentListViewStateCopyWith<$Res> { + _$TournamentListViewStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TournamentListViewState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? loading = null, + Object? groupTournaments = 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, + groupTournaments: null == groupTournaments + ? _value.groupTournaments + : groupTournaments // ignore: cast_nullable_to_non_nullable + as Map>, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TournamentListViewStateImplCopyWith<$Res> + implements $TournamentListViewStateCopyWith<$Res> { + factory _$$TournamentListViewStateImplCopyWith( + _$TournamentListViewStateImpl value, + $Res Function(_$TournamentListViewStateImpl) then) = + __$$TournamentListViewStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {Object? error, + bool loading, + Map> groupTournaments}); +} + +/// @nodoc +class __$$TournamentListViewStateImplCopyWithImpl<$Res> + extends _$TournamentListViewStateCopyWithImpl<$Res, + _$TournamentListViewStateImpl> + implements _$$TournamentListViewStateImplCopyWith<$Res> { + __$$TournamentListViewStateImplCopyWithImpl( + _$TournamentListViewStateImpl _value, + $Res Function(_$TournamentListViewStateImpl) _then) + : super(_value, _then); + + /// Create a copy of TournamentListViewState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? error = freezed, + Object? loading = null, + Object? groupTournaments = null, + }) { + return _then(_$TournamentListViewStateImpl( + error: freezed == error ? _value.error : error, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + groupTournaments: null == groupTournaments + ? _value._groupTournaments + : groupTournaments // ignore: cast_nullable_to_non_nullable + as Map>, + )); + } +} + +/// @nodoc + +class _$TournamentListViewStateImpl implements _TournamentListViewState { + const _$TournamentListViewStateImpl( + {this.error, + this.loading = true, + final Map> groupTournaments = const {}}) + : _groupTournaments = groupTournaments; + + @override + final Object? error; + @override + @JsonKey() + final bool loading; + final Map> _groupTournaments; + @override + @JsonKey() + Map> get groupTournaments { + if (_groupTournaments is EqualUnmodifiableMapView) return _groupTournaments; + // ignore: implicit_dynamic_type + return EqualUnmodifiableMapView(_groupTournaments); + } + + @override + String toString() { + return 'TournamentListViewState(error: $error, loading: $loading, groupTournaments: $groupTournaments)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TournamentListViewStateImpl && + const DeepCollectionEquality().equals(other.error, error) && + (identical(other.loading, loading) || other.loading == loading) && + const DeepCollectionEquality() + .equals(other._groupTournaments, _groupTournaments)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + const DeepCollectionEquality().hash(error), + loading, + const DeepCollectionEquality().hash(_groupTournaments)); + + /// Create a copy of TournamentListViewState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TournamentListViewStateImplCopyWith<_$TournamentListViewStateImpl> + get copyWith => __$$TournamentListViewStateImplCopyWithImpl< + _$TournamentListViewStateImpl>(this, _$identity); +} + +abstract class _TournamentListViewState implements TournamentListViewState { + const factory _TournamentListViewState( + {final Object? error, + final bool loading, + final Map> groupTournaments}) = + _$TournamentListViewStateImpl; + + @override + Object? get error; + @override + bool get loading; + @override + Map> get groupTournaments; + + /// Create a copy of TournamentListViewState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TournamentListViewStateImplCopyWith<_$TournamentListViewStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/style/lib/extensions/date_extensions.dart b/style/lib/extensions/date_extensions.dart index fa7b7a8f..f6f75f26 100644 --- a/style/lib/extensions/date_extensions.dart +++ b/style/lib/extensions/date_extensions.dart @@ -1,3 +1,5 @@ extension DateTimeExtensions on DateTime { DateTime get startOfDay => DateTime(year, month, day); + + DateTime get startOfMonth => DateTime(year, month, 1); } From 8d69b134a3963f7c9eb765ed39b7f7013260b264 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 4 Oct 2024 10:30:11 +0530 Subject: [PATCH 07/33] Update header size --- khelo/lib/ui/flow/tournament/tournament_list_screen.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index c0aeaf01..dfe45f28 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -259,8 +259,8 @@ class SliverAppbarDelegate extends SliverPersistentHeaderDelegate { } @override - double get maxExtent => 60; + double get maxExtent => 50; @override - double get minExtent => 60; + double get minExtent => 50; } From d2bfa8a97fd16be872ebdf075ad6659cd2c25baa Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 4 Oct 2024 10:41:55 +0530 Subject: [PATCH 08/33] Update tournament filter query --- data/lib/service/tournament/tournament_service.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index f07791ae..1e9b1977 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -43,10 +43,15 @@ class TournamentService { if (_currentUserId == null) { return Stream.value([]); } + final currantMember = TournamentMember(id: _currentUserId ?? 'INVALID ID'); + final memberContains = [ + currantMember.copyWith(role: TournamentMemberRole.organizer).toJson(), + currantMember.copyWith(role: TournamentMemberRole.admin).toJson(), + ]; final filter = Filter.or( Filter(FireStoreConst.createdBy, isEqualTo: _currentUserId), - Filter(FireStoreConst.members, arrayContains: _currentUserId), + Filter(FireStoreConst.members, arrayContainsAny: memberContains), ); return _tournamentCollection From 6d3541808f7b71a9c8a06d863383eaf241aff4c8 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 4 Oct 2024 11:57:39 +0530 Subject: [PATCH 09/33] Mr changes --- khelo/assets/locales/app_en.arb | 13 +++-- .../domain/extensions/enum_extensions.dart | 12 ++-- .../matches/add_match/add_match_screen.dart | 8 +-- .../tournament/add/add_tournament_screen.dart | 57 ++----------------- 4 files changed, 22 insertions(+), 68 deletions(-) diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2870c08b..2dbe2652 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -147,6 +147,7 @@ "add_tournament_screen_title": "Add Tournament", "add_tournament_name": "Tournament Name", "add_tournament_type": "Tournament Type", + "add_tournament_type_placeholder": "Select tournament type", "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", @@ -163,12 +164,12 @@ "tournament_type_mixed": "Mixed", "tournament_type_other": "Other", - "tournament_type_knockout_description": "Teams face off in a single elimination, with the loser immediately knocked out.", - "tournament_type_miniRobin_description": "A smaller round-robin format where each team plays once against all others.", - "tournament_type_boxLeague_description": "Teams are divided into groups, with top teams advancing to the knockout stage.", - "tournament_type_doubleOut_description": "Teams get two chances before being knocked out, with a winners and losers bracket.", - "tournament_type_superOver_description": "A knockout format with a super over to decide tied matches.", - "tournament_type_bestOf_description": "Teams play a series of matches, and the first to win the majority is the champion.", + "tournament_type_knock_out_description": "Teams face off in a single elimination, with the loser immediately knocked out.", + "tournament_type_mini_robin_description": "A smaller round-robin format where each team plays once against all others.", + "tournament_type_box_league_description": "Teams are divided into groups, with top teams advancing to the knockout stage.", + "tournament_type_double_out_description": "Teams get two chances before being knocked out, with a winners and losers bracket.", + "tournament_type_super_over_description": "A knockout format with a super over to decide tied matches.", + "tournament_type_best_of_description": "Teams play a series of matches, and the first to win the majority is the champion.", "tournament_type_gully_description": "Casual street-style cricket with short games and flexible rules.", "tournament_type_mixed_description": "Teams are randomly mixed, creating fun and unpredictable matches", "tournament_type_other_description": "A custom format for tournaments with unique rules or structures.", diff --git a/khelo/lib/domain/extensions/enum_extensions.dart b/khelo/lib/domain/extensions/enum_extensions.dart index 7901e6b0..e17de56a 100644 --- a/khelo/lib/domain/extensions/enum_extensions.dart +++ b/khelo/lib/domain/extensions/enum_extensions.dart @@ -294,17 +294,17 @@ extension TournamentTypeString on TournamentType { String getDescriptionString(BuildContext context) { switch (this) { case TournamentType.knockOut: - return context.l10n.tournament_type_knockout_description; + return context.l10n.tournament_type_knock_out_description; case TournamentType.miniRobin: - return context.l10n.tournament_type_miniRobin_description; + return context.l10n.tournament_type_mini_robin_description; case TournamentType.boxLeague: - return context.l10n.tournament_type_boxLeague_description; + return context.l10n.tournament_type_box_league_description; case TournamentType.doubleOut: - return context.l10n.tournament_type_doubleOut_description; + return context.l10n.tournament_type_double_out_description; case TournamentType.superOver: - return context.l10n.tournament_type_superOver_description; + return context.l10n.tournament_type_super_over_description; case TournamentType.bestOf: - return context.l10n.tournament_type_bestOf_description; + return context.l10n.tournament_type_best_of_description; case TournamentType.gully: return context.l10n.tournament_type_gully_description; case TournamentType.mixed: 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 95b250d3..7889c126 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 @@ -336,11 +336,11 @@ class _AddMatchScreenState extends ConsumerState { headerText: context.l10n.add_match_time_title, placeholder: context.l10n.add_match_time_title, onTap: () { - selectDate( + selectTime( context, - initialDate: state.matchTime, - onDateSelected: (selectedDate) => - notifier.onDateSelect(selectedDate: selectedDate), + initialTime: state.matchTime, + onTimeSelected: (selectedTime) => + notifier.onDateSelect(selectedDate: selectedTime), ); }), ), diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index 5646a766..68e0632b 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -8,13 +8,11 @@ import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/gen/assets.gen.dart'; import 'package:khelo/ui/flow/tournament/add/add_tournament_view_model.dart'; -import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/bottom_sticky_overlay.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/pickers/date_and_time_picker.dart'; import 'package:style/text/app_text_field.dart'; -import 'package:style/text/app_text_style.dart'; import 'package:style/widgets/adaptive_outlined_tile.dart'; import '../../../../components/action_bottom_sheet.dart'; @@ -97,10 +95,11 @@ class _AddTournamentScreenState extends ConsumerState { ), ), const SizedBox(height: 16), - _selectTileView( - label: context.l10n.add_tournament_type, + AdaptiveOutlinedTile( + placeholder: context.l10n.add_tournament_type_placeholder, + headerText: context.l10n.add_tournament_type, title: state.selectedType.getString(context), - icon: Icons.keyboard_arrow_down_rounded, + showTrailingIcon: true, onTap: () { _selectTypeSheet( context, @@ -124,7 +123,7 @@ class _AddTournamentScreenState extends ConsumerState { context.l10n.common_create, progress: state.loading, enabled: state.enableButton, - onPressed: () => notifier.addTournament(), + onPressed: notifier.addTournament, ), ); } @@ -152,52 +151,6 @@ class _AddTournamentScreenState extends ConsumerState { ); } - Widget _selectTileView({ - String? label, - IconData? icon, - required String title, - required Function() onTap, - }) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - if (label != null) ...[ - Text( - label, - style: AppTextStyle.body2.copyWith( - color: context.colorScheme.textDisabled, - ), - ), - const SizedBox(height: 12), - ], - OnTapScale( - onTap: onTap, - child: Material( - type: MaterialType.transparency, - child: ListTile( - dense: true, - contentPadding: const EdgeInsets.symmetric(horizontal: 12), - shape: RoundedRectangleBorder( - side: BorderSide(color: context.colorScheme.outline), - borderRadius: BorderRadius.circular(12), - ), - title: Text( - title, - style: AppTextStyle.body2.copyWith( - color: context.colorScheme.textPrimary, - ), - ), - trailing: Icon( - icon, - color: context.colorScheme.textDisabled, - ), - ), - ), - ), - ], - ); - } - Widget _dateScheduleView( BuildContext context, AddTournamentState state, From a68c92a5762cc27875ec57ef1dfc513cfd054db3 Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 4 Oct 2024 12:18:22 +0530 Subject: [PATCH 10/33] Mr changes --- .../service/tournament/tournament_service.dart | 15 +++++---------- .../lib/utils/constant/firestore_constant.dart | 2 +- khelo/assets/locales/app_en.arb | 2 +- .../tournament/tournament_list_screen.dart | 4 ++-- .../tournament/tournament_list_view_model.dart | 18 ++++++++---------- 5 files changed, 17 insertions(+), 24 deletions(-) diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index 1e9b1977..ef904ffe 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -43,11 +43,11 @@ class TournamentService { if (_currentUserId == null) { return Stream.value([]); } - final currantMember = TournamentMember(id: _currentUserId ?? 'INVALID ID'); + final currentMember = TournamentMember(id: _currentUserId ?? 'INVALID ID'); final memberContains = [ - currantMember.copyWith(role: TournamentMemberRole.organizer).toJson(), - currantMember.copyWith(role: TournamentMemberRole.admin).toJson(), + currentMember.copyWith(role: TournamentMemberRole.organizer).toJson(), + currentMember.copyWith(role: TournamentMemberRole.admin).toJson(), ]; final filter = Filter.or( Filter(FireStoreConst.createdBy, isEqualTo: _currentUserId), @@ -57,12 +57,7 @@ class TournamentService { return _tournamentCollection .where(filter) .snapshots() - .asyncMap((snapshot) async { - return await Future.wait( - snapshot.docs.map((mainDoc) async { - return mainDoc.data(); - }).toList(), - ); - }).handleError((error, stack) => throw AppError.fromError(error, stack)); + .map((event) => event.docs.map((e) => e.data()).toList()) + .handleError((error, stack) => throw AppError.fromError(error, stack)); } } diff --git a/data/lib/utils/constant/firestore_constant.dart b/data/lib/utils/constant/firestore_constant.dart index 9539c7ad..76f9f142 100644 --- a/data/lib/utils/constant/firestore_constant.dart +++ b/data/lib/utils/constant/firestore_constant.dart @@ -52,7 +52,7 @@ class FireStoreConst { static const String phone = "phone"; static const String name = "name"; - // tournament filed const + // tournament field const static const String members = "members"; } diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index e02bf22c..2ef2df2e 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -150,7 +150,7 @@ "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", - "tournament_list_no_tournaments_created_title": "No tournaments created", + "tournament_list_empty_list_title": "No tournaments created", "tournament_list_empty_list_description": "Tap on the “ + ” icon to create a tournament", "tournament_list_match_title": "{count, plural, =0{No matches} =1{{count} Match} other{{count} Matches}}", "@tournament_list_match_title": { diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index dfe45f28..5470bb58 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -62,12 +62,12 @@ class _TournamentListScreenState extends ConsumerState if (state.error != null) { return ErrorScreen( error: state.error, - onRetryTap: notifier.onResume, + onRetryTap: notifier.loadTournaments, ); } if (state.groupTournaments.isEmpty) { return EmptyScreen( - title: context.l10n.tournament_list_no_tournaments_created_title, + title: context.l10n.tournament_list_empty_list_title, description: context.l10n.tournament_list_empty_list_description, isShowButton: false, ); diff --git a/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart index a23781e2..45606b32 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart @@ -17,14 +17,15 @@ final tournamentListViewStateProvider = class TournamentListViewNotifier extends StateNotifier { final TournamentService _tournamentService; - late StreamSubscription _tournamentStreamSubscription; + StreamSubscription? _tournamentStreamSubscription; TournamentListViewNotifier(this._tournamentService) : super(const TournamentListViewState()) { - _loadTournamentList(); + loadTournaments(); } - Future _loadTournamentList() async { + Future loadTournaments() async { + _tournamentStreamSubscription?.cancel(); state = state.copyWith(loading: state.groupTournaments.isEmpty); try { _tournamentStreamSubscription = _tournamentService @@ -53,13 +54,10 @@ class TournamentListViewNotifier ); } - _cancelStreamSubscription() { - _tournamentStreamSubscription.cancel(); - } - - onResume() { - _cancelStreamSubscription(); - _loadTournamentList(); + @override + void dispose() { + _tournamentStreamSubscription?.cancel(); + super.dispose(); } } From beaf140215a100fbf95cc80a5029eddd67884e5e Mon Sep 17 00:00:00 2001 From: cp-mayank-v Date: Fri, 4 Oct 2024 13:59:29 +0530 Subject: [PATCH 11/33] Mr changes --- khelo/assets/locales/app_en.arb | 12 ++++++------ khelo/lib/domain/extensions/enum_extensions.dart | 12 ++++++------ style/lib/pickers/date_and_time_picker.dart | 4 ++-- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2dbe2652..c1dcb8dc 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -154,12 +154,12 @@ "@_TOURNAMENT_TYPE":{ }, - "tournament_type_knockout": "KnockOut", - "tournament_type_miniRobin": "Mini Robin", - "tournament_type_boxLeague": "Box League", - "tournament_type_doubleOut": "Double Out", - "tournament_type_superOver": "Super Over", - "tournament_type_bestOf": "Best Of", + "tournament_type_knock_out": "Knockout", + "tournament_type_mini_robin": "Mini Robin", + "tournament_type_box_league": "Box League", + "tournament_type_double_out": "Double Out", + "tournament_type_super_over": "Super Over", + "tournament_type_best_of": "Best Of", "tournament_type_gully": "Gully", "tournament_type_mixed": "Mixed", "tournament_type_other": "Other", diff --git a/khelo/lib/domain/extensions/enum_extensions.dart b/khelo/lib/domain/extensions/enum_extensions.dart index e17de56a..7db17e44 100644 --- a/khelo/lib/domain/extensions/enum_extensions.dart +++ b/khelo/lib/domain/extensions/enum_extensions.dart @@ -271,17 +271,17 @@ extension TournamentTypeString on TournamentType { String getString(BuildContext context) { switch (this) { case TournamentType.knockOut: - return context.l10n.tournament_type_knockout; + return context.l10n.tournament_type_knock_out; case TournamentType.miniRobin: - return context.l10n.tournament_type_miniRobin; + return context.l10n.tournament_type_mini_robin; case TournamentType.boxLeague: - return context.l10n.tournament_type_boxLeague; + return context.l10n.tournament_type_box_league; case TournamentType.doubleOut: - return context.l10n.tournament_type_doubleOut; + return context.l10n.tournament_type_double_out; case TournamentType.superOver: - return context.l10n.tournament_type_superOver; + return context.l10n.tournament_type_super_over; case TournamentType.bestOf: - return context.l10n.tournament_type_bestOf; + return context.l10n.tournament_type_best_of; case TournamentType.gully: return context.l10n.tournament_type_gully; case TournamentType.mixed: diff --git a/style/lib/pickers/date_and_time_picker.dart b/style/lib/pickers/date_and_time_picker.dart index 549cbc0e..4357d614 100644 --- a/style/lib/pickers/date_and_time_picker.dart +++ b/style/lib/pickers/date_and_time_picker.dart @@ -13,8 +13,8 @@ Future selectDate( context: context, helpText: helpText, initialDate: initialDate, - firstDate: DateTime(1965), - lastDate: DateTime(2101), + firstDate: DateTime.now(), + lastDate: DateTime(DateTime.now().year + 1), builder: (context, child) { return Theme( data: context.brightness == Brightness.dark From 9f6937f7314340a0092d0bb8e1e5632915c8291b Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 9 Oct 2024 16:32:32 +0530 Subject: [PATCH 12/33] Add banner support --- data/lib/api/tournament/tournament_model.dart | 1 + .../tournament/tournament_model.freezed.dart | 66 ++++- .../api/tournament/tournament_model.g.dart | 2 + .../constant/firebase_storage_constant.dart | 8 +- khelo/assets/locales/app_en.arb | 2 + .../lib/components/profile_image_avatar.dart | 20 +- khelo/lib/ui/app_route.dart | 1 + .../tournament/add/add_tournament_screen.dart | 237 ++++++++++++++---- .../add/add_tournament_view_model.dart | 38 ++- .../add_tournament_view_model.freezed.dart | 119 ++++++--- 10 files changed, 374 insertions(+), 120 deletions(-) diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index b753644e..07b9c4c7 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -18,6 +18,7 @@ class TournamentModel with _$TournamentModel { required String id, required String name, String? profile_img_url, + String? banner_img_url, required TournamentType type, @Default([]) List members, required String created_by, diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index e9e58ef1..9eba15b3 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -23,6 +23,7 @@ mixin _$TournamentModel { String get id => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; String? get profile_img_url => throw _privateConstructorUsedError; + String? get banner_img_url => throw _privateConstructorUsedError; TournamentType get type => throw _privateConstructorUsedError; List get members => throw _privateConstructorUsedError; String get created_by => throw _privateConstructorUsedError; @@ -39,8 +40,12 @@ mixin _$TournamentModel { @JsonKey(includeFromJson: false, includeToJson: false) List get matches => throw _privateConstructorUsedError; + /// Serializes this TournamentModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of TournamentModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TournamentModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -55,6 +60,7 @@ abstract class $TournamentModelCopyWith<$Res> { {String id, String name, String? profile_img_url, + String? banner_img_url, TournamentType type, List members, String created_by, @@ -79,12 +85,15 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TournamentModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? id = null, Object? name = null, Object? profile_img_url = freezed, + Object? banner_img_url = freezed, Object? type = null, Object? members = null, Object? created_by = null, @@ -109,6 +118,10 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> ? _value.profile_img_url : profile_img_url // ignore: cast_nullable_to_non_nullable as String?, + banner_img_url: freezed == banner_img_url + ? _value.banner_img_url + : banner_img_url // ignore: cast_nullable_to_non_nullable + as String?, type: null == type ? _value.type : type // ignore: cast_nullable_to_non_nullable @@ -165,6 +178,7 @@ abstract class _$$TournamentModelImplCopyWith<$Res> {String id, String name, String? profile_img_url, + String? banner_img_url, TournamentType type, List members, String created_by, @@ -187,12 +201,15 @@ class __$$TournamentModelImplCopyWithImpl<$Res> _$TournamentModelImpl _value, $Res Function(_$TournamentModelImpl) _then) : super(_value, _then); + /// Create a copy of TournamentModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ Object? id = null, Object? name = null, Object? profile_img_url = freezed, + Object? banner_img_url = freezed, Object? type = null, Object? members = null, Object? created_by = null, @@ -217,6 +234,10 @@ class __$$TournamentModelImplCopyWithImpl<$Res> ? _value.profile_img_url : profile_img_url // ignore: cast_nullable_to_non_nullable as String?, + banner_img_url: freezed == banner_img_url + ? _value.banner_img_url + : banner_img_url // ignore: cast_nullable_to_non_nullable + as String?, type: null == type ? _value.type : type // ignore: cast_nullable_to_non_nullable @@ -269,6 +290,7 @@ class _$TournamentModelImpl implements _TournamentModel { {required this.id, required this.name, this.profile_img_url, + this.banner_img_url, required this.type, final List members = const [], required this.created_by, @@ -297,6 +319,8 @@ class _$TournamentModelImpl implements _TournamentModel { @override final String? profile_img_url; @override + final String? banner_img_url; + @override final TournamentType type; final List _members; @override @@ -356,7 +380,7 @@ class _$TournamentModelImpl implements _TournamentModel { @override String toString() { - return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, type: $type, members: $members, created_by: $created_by, created_at: $created_at, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; + return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, banner_img_url: $banner_img_url, type: $type, members: $members, created_by: $created_by, created_at: $created_at, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; } @override @@ -368,6 +392,8 @@ class _$TournamentModelImpl implements _TournamentModel { (identical(other.name, name) || other.name == name) && (identical(other.profile_img_url, profile_img_url) || other.profile_img_url == profile_img_url) && + (identical(other.banner_img_url, banner_img_url) || + other.banner_img_url == banner_img_url) && (identical(other.type, type) || other.type == type) && const DeepCollectionEquality().equals(other._members, _members) && (identical(other.created_by, created_by) || @@ -385,13 +411,14 @@ class _$TournamentModelImpl implements _TournamentModel { const DeepCollectionEquality().equals(other._matches, _matches)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, id, name, profile_img_url, + banner_img_url, type, const DeepCollectionEquality().hash(_members), created_by, @@ -403,7 +430,9 @@ class _$TournamentModelImpl implements _TournamentModel { const DeepCollectionEquality().hash(_teams), const DeepCollectionEquality().hash(_matches)); - @JsonKey(ignore: true) + /// Create a copy of TournamentModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TournamentModelImplCopyWith<_$TournamentModelImpl> get copyWith => @@ -423,6 +452,7 @@ abstract class _TournamentModel implements TournamentModel { {required final String id, required final String name, final String? profile_img_url, + final String? banner_img_url, required final TournamentType type, final List members, required final String created_by, @@ -446,6 +476,8 @@ abstract class _TournamentModel implements TournamentModel { @override String? get profile_img_url; @override + String? get banner_img_url; + @override TournamentType get type; @override List get members; @@ -470,8 +502,11 @@ abstract class _TournamentModel implements TournamentModel { @override @JsonKey(includeFromJson: false, includeToJson: false) List get matches; + + /// Create a copy of TournamentModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TournamentModelImplCopyWith<_$TournamentModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -485,8 +520,12 @@ mixin _$TournamentMember { String get id => throw _privateConstructorUsedError; TournamentMemberRole get role => throw _privateConstructorUsedError; + /// Serializes this TournamentMember to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TournamentMemberCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -510,6 +549,8 @@ class _$TournamentMemberCopyWithImpl<$Res, $Val extends TournamentMember> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -548,6 +589,8 @@ class __$$TournamentMemberImplCopyWithImpl<$Res> $Res Function(_$TournamentMemberImpl) _then) : super(_value, _then); + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -596,11 +639,13 @@ class _$TournamentMemberImpl implements _TournamentMember { (identical(other.role, role) || other.role == role)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, id, role); - @JsonKey(ignore: true) + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TournamentMemberImplCopyWith<_$TournamentMemberImpl> get copyWith => @@ -627,8 +672,11 @@ abstract class _TournamentMember implements TournamentMember { String get id; @override TournamentMemberRole get role; + + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TournamentMemberImplCopyWith<_$TournamentMemberImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index 2ed63445..475a8cd0 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -11,6 +11,7 @@ _$TournamentModelImpl _$$TournamentModelImplFromJson(Map json) => id: json['id'] as String, name: json['name'] as String, profile_img_url: json['profile_img_url'] as String?, + banner_img_url: json['banner_img_url'] as String?, type: $enumDecode(_$TournamentTypeEnumMap, json['type']), members: (json['members'] as List?) ?.map((e) => TournamentMember.fromJson( @@ -40,6 +41,7 @@ Map _$$TournamentModelImplToJson( 'id': instance.id, 'name': instance.name, 'profile_img_url': instance.profile_img_url, + 'banner_img_url': instance.banner_img_url, 'type': _$TournamentTypeEnumMap[instance.type]!, 'members': instance.members.map((e) => e.toJson()).toList(), 'created_by': instance.created_by, diff --git a/data/lib/utils/constant/firebase_storage_constant.dart b/data/lib/utils/constant/firebase_storage_constant.dart index 5130b97d..2c1cd5be 100644 --- a/data/lib/utils/constant/firebase_storage_constant.dart +++ b/data/lib/utils/constant/firebase_storage_constant.dart @@ -22,5 +22,11 @@ class StorageConst { required String userId, required String tournamentId, }) => - "$rootDirectory/$userId/tournament_profile_images/$tournamentId"; + "$rootDirectory/$userId/tournament_images/$tournamentId/profile"; + + static String tournamentBannerUploadPath({ + required String userId, + required String tournamentId, + }) => + "$rootDirectory/$userId/tournament_images/$tournamentId/banner"; } diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 48575b41..2ce79083 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -151,6 +151,8 @@ "add_tournament_type_placeholder": "Select tournament type", "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", + "add_tournament_edit_banner": "Edit banner", + "add_tournament_add_banner_placeholder": "Add banner image", "tournament_list_empty_list_title": "No tournaments created", "tournament_list_empty_list_description": "Tap on the “ + ” icon to create a tournament", diff --git a/khelo/lib/components/profile_image_avatar.dart b/khelo/lib/components/profile_image_avatar.dart index cec0b715..7cb30b38 100644 --- a/khelo/lib/components/profile_image_avatar.dart +++ b/khelo/lib/components/profile_image_avatar.dart @@ -14,11 +14,8 @@ class ProfileImageAvatar extends StatelessWidget { final String? imageUrl; final String? filePath; final String? placeHolderImage; - final Color? placeHolderColor; final bool isLoading; - final BoxShape shape; - final Color? color; - final BorderRadius? borderRadius; + final Alignment alignment; final Function() onEditButtonTap; const ProfileImageAvatar({ @@ -27,17 +24,15 @@ class ProfileImageAvatar extends StatelessWidget { this.imageUrl, this.filePath, this.placeHolderImage, - this.placeHolderColor, required this.isLoading, - this.shape = BoxShape.circle, - this.borderRadius, - this.color, required this.onEditButtonTap, + this.alignment = Alignment.center, }); @override Widget build(BuildContext context) { - return Center( + return Align( + alignment: alignment, child: SizedBox( height: size, width: size, @@ -57,9 +52,8 @@ class ProfileImageAvatar extends StatelessWidget { width: size, alignment: Alignment.center, decoration: BoxDecoration( - shape: shape, - borderRadius: borderRadius, - color: color ?? context.colorScheme.primary, + shape: BoxShape.circle, + color: context.colorScheme.primary, image: (filePath != null) ? DecorationImage( image: FileImage(File(filePath!)), @@ -102,7 +96,7 @@ class ProfileImageAvatar extends StatelessWidget { height: size / 3, width: size / 3, colorFilter: ColorFilter.mode( - placeHolderColor ?? context.colorScheme.textInversePrimary, + context.colorScheme.textInversePrimary, BlendMode.srcATop, ), ); diff --git a/khelo/lib/ui/app_route.dart b/khelo/lib/ui/app_route.dart index 6e15f389..4b90d3f6 100644 --- a/khelo/lib/ui/app_route.dart +++ b/khelo/lib/ui/app_route.dart @@ -54,6 +54,7 @@ class AppRoute { static const pathSearchHome = "/search-home"; static const pathViewAll = "/view-all"; static const pathContactSelection = "/contact-selection"; + static const pathAddTournament = "/add-tournament"; final String path; final String? name; diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index 68e0632b..cc942bcb 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -1,3 +1,6 @@ +import 'dart:io'; + +import 'package:cached_network_image/cached_network_image.dart'; import 'package:data/api/tournament/tournament_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -8,11 +11,14 @@ import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/gen/assets.gen.dart'; import 'package:khelo/ui/flow/tournament/add/add_tournament_view_model.dart'; +import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/bottom_sticky_overlay.dart'; import 'package:style/button/primary_button.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/indicator/progress_indicator.dart'; import 'package:style/pickers/date_and_time_picker.dart'; import 'package:style/text/app_text_field.dart'; +import 'package:style/text/app_text_style.dart'; import 'package:style/widgets/adaptive_outlined_tile.dart'; import '../../../../components/action_bottom_sheet.dart'; @@ -64,10 +70,10 @@ class _AddTournamentScreenState extends ConsumerState { final state = ref.watch(addTournamentStateProvider); return AppPage( - title: context.l10n.add_tournament_screen_title, body: Builder( builder: (context) => Stack( children: [ + _bannerView(context, state), _body(context, state), _stickyButton(context, state), ], @@ -77,40 +83,180 @@ class _AddTournamentScreenState extends ConsumerState { } Widget _body(BuildContext context, AddTournamentState state) { - return ListView( - padding: context.mediaQueryPadding + const EdgeInsets.all(16), - children: [ - _profileView(context, state), - AppTextField( - controller: state.nameController, - label: context.l10n.add_tournament_name, - onChanged: (_) => notifier.onChange(), - contentPadding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - borderRadius: BorderRadius.circular(12), - borderType: AppTextFieldBorderType.outline, - borderColor: BorderColor( - focusColor: context.colorScheme.outline, - unFocusColor: context.colorScheme.outline, + return Padding( + padding: EdgeInsets.all(16) + .copyWith(top: context.mediaQuerySize.height * 0.22), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _profileView(context, state), + const SizedBox(height: 16), + AppTextField( + controller: state.nameController, + label: context.l10n.add_tournament_name, + onChanged: (_) => notifier.onChange(), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + borderRadius: BorderRadius.circular(12), + borderType: AppTextFieldBorderType.outline, + borderColor: BorderColor( + focusColor: context.colorScheme.outline, + unFocusColor: context.colorScheme.outline, + ), + ), + const SizedBox(height: 16), + AdaptiveOutlinedTile( + placeholder: context.l10n.add_tournament_type_placeholder, + headerText: context.l10n.add_tournament_type, + title: state.selectedType.getString(context), + showTrailingIcon: true, + onTap: () { + _selectTypeSheet( + context, + selectedType: state.selectedType, + onSelected: (type) => notifier.onSelectType(type), + ); + }, + ), + const SizedBox(height: 16), + _dateScheduleView(context, state) + ], + ), + ), + ); + } + + Widget _bannerView(BuildContext context, AddTournamentState state) { + final double bannerHeight = context.mediaQuerySize.height * 0.27; + final bool hasBannerImage = + state.bannerPath != null || state.bannerImgUrl != null; + + return Container( + height: bannerHeight, + width: double.infinity, + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + image: state.bannerPath != null + ? DecorationImage( + image: FileImage(File(state.bannerPath!)), + fit: BoxFit.cover, + ) + : null, + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (state.imageUploading) Center(child: AppProgressIndicator()), + if (!state.imageUploading) + hasBannerImage + ? _buildCachedNetworkOrFileImage(context, state) + : _bannerPlaceholder(context, + onTap: () => _pickImage(isBanner: true)), + if (hasBannerImage && !state.imageUploading) + Positioned( + bottom: 16, + right: 16, + child: _editBannerButton(context), + ), + _buildHeader(context), + ], + ), + ); + } + + Widget _buildHeader(BuildContext context) { + return Positioned( + top: 16, + left: 0, + child: Padding( + padding: context.mediaQueryPadding, + child: Row( + children: [ + BackButton(color: context.colorScheme.textPrimary), + const SizedBox(width: 16), + Text( + context.l10n.add_tournament_screen_title, + style: AppTextStyle.header2.copyWith( + color: context.colorScheme.textPrimary, + ), + ) + ], + ), + ), + ); + } + + Widget _buildCachedNetworkOrFileImage( + BuildContext context, AddTournamentState state) { + if (state.bannerPath != null) { + return Image.file( + File(state.bannerPath!), + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ); + } else if (state.bannerImgUrl != null) { + return CachedNetworkImage( + imageUrl: state.bannerImgUrl!, + fit: BoxFit.cover, + width: double.infinity, + height: double.infinity, + ); + } + return SizedBox.shrink(); // Fallback if neither image is available + } + + Widget _editBannerButton(BuildContext context) { + return TextButton.icon( + onPressed: () => _pickImage(isBanner: true), + icon: SvgPicture.asset( + Assets.images.icCamera, + colorFilter: ColorFilter.mode( + context.colorScheme.surface, + BlendMode.srcATop, + ), + ), + label: Text( + context.l10n.add_tournament_edit_banner, + style: + AppTextStyle.caption.copyWith(color: context.colorScheme.surface), + ), + style: TextButton.styleFrom( + backgroundColor: context.colorScheme.textDisabled, + ), + ); + } + + void _pickImage({bool isBanner = false}) async { + final imagePath = await ImagePickerSheet.show(context, true); + if (imagePath != null) { + notifier.onImageChange(imagePath: imagePath, isBanner: isBanner); + } + } + + Widget _bannerPlaceholder( + BuildContext context, { + required Function() onTap, + }) { + return OnTapScale( + onTap: onTap, + child: Column(mainAxisAlignment: MainAxisAlignment.center, children: [ + SvgPicture.asset( + Assets.images.icCamera, + colorFilter: ColorFilter.mode( + context.colorScheme.textDisabled, + BlendMode.srcATop, ), ), - const SizedBox(height: 16), - AdaptiveOutlinedTile( - placeholder: context.l10n.add_tournament_type_placeholder, - headerText: context.l10n.add_tournament_type, - title: state.selectedType.getString(context), - showTrailingIcon: true, - onTap: () { - _selectTypeSheet( - context, - selectedType: state.selectedType, - onSelected: (type) => notifier.onSelectType(type), - ); - }, + const SizedBox(height: 4), + Text( + context.l10n.add_tournament_add_banner_placeholder, + style: AppTextStyle.caption.copyWith( + color: context.colorScheme.textDisabled, + ), ), - const SizedBox(height: 16), - _dateScheduleView(context, state), - ], + ]), ); } @@ -129,25 +275,14 @@ class _AddTournamentScreenState extends ConsumerState { } Widget _profileView(BuildContext context, AddTournamentState state) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 24.0), - child: ProfileImageAvatar( - size: 120, - isLoading: state.imageUploading && state.filePath == null, - shape: BoxShape.rectangle, - filePath: state.filePath, - imageUrl: state.imageUrl, - placeHolderImage: Assets.images.icTournaments, - placeHolderColor: context.colorScheme.textPrimary, - color: context.colorScheme.containerHigh, - borderRadius: BorderRadius.circular(16), - onEditButtonTap: () async { - final imagePath = await ImagePickerSheet.show(context, true); - if (imagePath != null) { - notifier.onImageChange(imagePath); - } - }, - ), + return ProfileImageAvatar( + size: 90, + isLoading: state.imageUploading && state.profilePath == null, + filePath: state.profilePath, + imageUrl: state.profileImgUrl, + placeHolderImage: Assets.images.icTournaments, + alignment: Alignment.centerLeft, + onEditButtonTap: () => _pickImage(), ); } diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index 7dac026e..f4ff7d50 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -45,11 +45,18 @@ class AddTournamentViewNotifier extends StateNotifier { state = state.copyWith(currentUserId: userId); } - Future onImageChange(String imagePath) async { + Future onImageChange({ + required String imagePath, + bool isBanner = false, + }) async { try { state = state.copyWith(imageUploading: true, actionError: null); if (await File(imagePath).isFileUnderMaxSize()) { - state = state.copyWith(filePath: imagePath, imageUploading: false); + isBanner + ? state = + state.copyWith(bannerPath: imagePath, imageUploading: false) + : state = + state.copyWith(profilePath: imagePath, imageUploading: false); } else { state = state.copyWith( imageUploading: false, @@ -87,14 +94,24 @@ class AddTournamentViewNotifier extends StateNotifier { final tournamentId = _tournamentService.generateTournamentId; final name = state.nameController.text.trim(); - if (state.filePath != null && state.currentUserId != null) { - final imageUrl = await _fileUploadService.uploadProfileImage( - filePath: state.filePath ?? '', + if (state.profilePath != null) { + final profileImgUrl = await _fileUploadService.uploadProfileImage( + filePath: state.profilePath ?? '', uploadPath: StorageConst.tournamentProfileUploadPath( userId: state.currentUserId ?? 'INVALID ID', tournamentId: tournamentId), ); - state = state.copyWith(imageUrl: imageUrl); + state = state.copyWith(profileImgUrl: profileImgUrl); + } + + if (state.bannerPath != null) { + final bannerImgUrl = await _fileUploadService.uploadProfileImage( + filePath: state.bannerPath ?? '', + uploadPath: StorageConst.tournamentBannerUploadPath( + userId: state.currentUserId ?? 'INVALID ID', + tournamentId: tournamentId), + ); + state = state.copyWith(bannerImgUrl: bannerImgUrl); } final tournament = TournamentModel( @@ -111,7 +128,8 @@ class AddTournamentViewNotifier extends StateNotifier { role: TournamentMemberRole.organizer, ), ], - profile_img_url: state.imageUrl, + profile_img_url: state.profileImgUrl, + banner_img_url: state.bannerImgUrl, ); await _tournamentService.createTournament(tournament); @@ -130,7 +148,8 @@ class AddTournamentState with _$AddTournamentState { const factory AddTournamentState({ Object? error, Object? actionError, - String? filePath, + String? profilePath, + String? bannerPath, String? currentUserId, required DateTime endDate, required DateTime startDate, @@ -138,7 +157,8 @@ class AddTournamentState with _$AddTournamentState { @Default(false) bool loading, @Default(false) bool enableButton, @Default(false) bool imageUploading, - @Default(null) String? imageUrl, + @Default(null) String? profileImgUrl, + @Default(null) String? bannerImgUrl, required TextEditingController nameController, @Default(TournamentType.knockOut) TournamentType selectedType, }) = _AddTournamentState; diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart index 8be804bc..23f8cfae 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -18,7 +18,8 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$AddTournamentState { Object? get error => throw _privateConstructorUsedError; Object? get actionError => throw _privateConstructorUsedError; - String? get filePath => throw _privateConstructorUsedError; + String? get profilePath => throw _privateConstructorUsedError; + String? get bannerPath => throw _privateConstructorUsedError; String? get currentUserId => throw _privateConstructorUsedError; DateTime get endDate => throw _privateConstructorUsedError; DateTime get startDate => throw _privateConstructorUsedError; @@ -26,7 +27,8 @@ mixin _$AddTournamentState { bool get loading => throw _privateConstructorUsedError; bool get enableButton => throw _privateConstructorUsedError; bool get imageUploading => throw _privateConstructorUsedError; - String? get imageUrl => throw _privateConstructorUsedError; + String? get profileImgUrl => throw _privateConstructorUsedError; + String? get bannerImgUrl => throw _privateConstructorUsedError; TextEditingController get nameController => throw _privateConstructorUsedError; TournamentType get selectedType => throw _privateConstructorUsedError; @@ -47,7 +49,8 @@ abstract class $AddTournamentStateCopyWith<$Res> { $Res call( {Object? error, Object? actionError, - String? filePath, + String? profilePath, + String? bannerPath, String? currentUserId, DateTime endDate, DateTime startDate, @@ -55,7 +58,8 @@ abstract class $AddTournamentStateCopyWith<$Res> { bool loading, bool enableButton, bool imageUploading, - String? imageUrl, + String? profileImgUrl, + String? bannerImgUrl, TextEditingController nameController, TournamentType selectedType}); } @@ -77,7 +81,8 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> $Res call({ Object? error = freezed, Object? actionError = freezed, - Object? filePath = freezed, + Object? profilePath = freezed, + Object? bannerPath = freezed, Object? currentUserId = freezed, Object? endDate = null, Object? startDate = null, @@ -85,16 +90,21 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> Object? loading = null, Object? enableButton = null, Object? imageUploading = null, - Object? imageUrl = freezed, + Object? profileImgUrl = freezed, + Object? bannerImgUrl = freezed, Object? nameController = null, Object? selectedType = null, }) { return _then(_value.copyWith( error: freezed == error ? _value.error : error, actionError: freezed == actionError ? _value.actionError : actionError, - filePath: freezed == filePath - ? _value.filePath - : filePath // ignore: cast_nullable_to_non_nullable + profilePath: freezed == profilePath + ? _value.profilePath + : profilePath // ignore: cast_nullable_to_non_nullable + as String?, + bannerPath: freezed == bannerPath + ? _value.bannerPath + : bannerPath // ignore: cast_nullable_to_non_nullable as String?, currentUserId: freezed == currentUserId ? _value.currentUserId @@ -124,9 +134,13 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> ? _value.imageUploading : imageUploading // ignore: cast_nullable_to_non_nullable as bool, - imageUrl: freezed == imageUrl - ? _value.imageUrl - : imageUrl // ignore: cast_nullable_to_non_nullable + profileImgUrl: freezed == profileImgUrl + ? _value.profileImgUrl + : profileImgUrl // ignore: cast_nullable_to_non_nullable + as String?, + bannerImgUrl: freezed == bannerImgUrl + ? _value.bannerImgUrl + : bannerImgUrl // ignore: cast_nullable_to_non_nullable as String?, nameController: null == nameController ? _value.nameController @@ -151,7 +165,8 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> $Res call( {Object? error, Object? actionError, - String? filePath, + String? profilePath, + String? bannerPath, String? currentUserId, DateTime endDate, DateTime startDate, @@ -159,7 +174,8 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> bool loading, bool enableButton, bool imageUploading, - String? imageUrl, + String? profileImgUrl, + String? bannerImgUrl, TextEditingController nameController, TournamentType selectedType}); } @@ -179,7 +195,8 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> $Res call({ Object? error = freezed, Object? actionError = freezed, - Object? filePath = freezed, + Object? profilePath = freezed, + Object? bannerPath = freezed, Object? currentUserId = freezed, Object? endDate = null, Object? startDate = null, @@ -187,16 +204,21 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> Object? loading = null, Object? enableButton = null, Object? imageUploading = null, - Object? imageUrl = freezed, + Object? profileImgUrl = freezed, + Object? bannerImgUrl = freezed, Object? nameController = null, Object? selectedType = null, }) { return _then(_$AddTournamentStateImpl( error: freezed == error ? _value.error : error, actionError: freezed == actionError ? _value.actionError : actionError, - filePath: freezed == filePath - ? _value.filePath - : filePath // ignore: cast_nullable_to_non_nullable + profilePath: freezed == profilePath + ? _value.profilePath + : profilePath // ignore: cast_nullable_to_non_nullable + as String?, + bannerPath: freezed == bannerPath + ? _value.bannerPath + : bannerPath // ignore: cast_nullable_to_non_nullable as String?, currentUserId: freezed == currentUserId ? _value.currentUserId @@ -226,9 +248,13 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> ? _value.imageUploading : imageUploading // ignore: cast_nullable_to_non_nullable as bool, - imageUrl: freezed == imageUrl - ? _value.imageUrl - : imageUrl // ignore: cast_nullable_to_non_nullable + profileImgUrl: freezed == profileImgUrl + ? _value.profileImgUrl + : profileImgUrl // ignore: cast_nullable_to_non_nullable + as String?, + bannerImgUrl: freezed == bannerImgUrl + ? _value.bannerImgUrl + : bannerImgUrl // ignore: cast_nullable_to_non_nullable as String?, nameController: null == nameController ? _value.nameController @@ -248,7 +274,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { const _$AddTournamentStateImpl( {this.error, this.actionError, - this.filePath, + this.profilePath, + this.bannerPath, this.currentUserId, required this.endDate, required this.startDate, @@ -256,7 +283,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { this.loading = false, this.enableButton = false, this.imageUploading = false, - this.imageUrl = null, + this.profileImgUrl = null, + this.bannerImgUrl = null, required this.nameController, this.selectedType = TournamentType.knockOut}); @@ -265,7 +293,9 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override final Object? actionError; @override - final String? filePath; + final String? profilePath; + @override + final String? bannerPath; @override final String? currentUserId; @override @@ -286,7 +316,10 @@ class _$AddTournamentStateImpl implements _AddTournamentState { final bool imageUploading; @override @JsonKey() - final String? imageUrl; + final String? profileImgUrl; + @override + @JsonKey() + final String? bannerImgUrl; @override final TextEditingController nameController; @override @@ -295,7 +328,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override String toString() { - return 'AddTournamentState(error: $error, actionError: $actionError, filePath: $filePath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, imageUploading: $imageUploading, imageUrl: $imageUrl, nameController: $nameController, selectedType: $selectedType)'; + return 'AddTournamentState(error: $error, actionError: $actionError, profilePath: $profilePath, bannerPath: $bannerPath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, imageUploading: $imageUploading, profileImgUrl: $profileImgUrl, bannerImgUrl: $bannerImgUrl, nameController: $nameController, selectedType: $selectedType)'; } @override @@ -306,8 +339,10 @@ class _$AddTournamentStateImpl implements _AddTournamentState { const DeepCollectionEquality().equals(other.error, error) && const DeepCollectionEquality() .equals(other.actionError, actionError) && - (identical(other.filePath, filePath) || - other.filePath == filePath) && + (identical(other.profilePath, profilePath) || + other.profilePath == profilePath) && + (identical(other.bannerPath, bannerPath) || + other.bannerPath == bannerPath) && (identical(other.currentUserId, currentUserId) || other.currentUserId == currentUserId) && (identical(other.endDate, endDate) || other.endDate == endDate) && @@ -319,8 +354,10 @@ class _$AddTournamentStateImpl implements _AddTournamentState { other.enableButton == enableButton) && (identical(other.imageUploading, imageUploading) || other.imageUploading == imageUploading) && - (identical(other.imageUrl, imageUrl) || - other.imageUrl == imageUrl) && + (identical(other.profileImgUrl, profileImgUrl) || + other.profileImgUrl == profileImgUrl) && + (identical(other.bannerImgUrl, bannerImgUrl) || + other.bannerImgUrl == bannerImgUrl) && (identical(other.nameController, nameController) || other.nameController == nameController) && (identical(other.selectedType, selectedType) || @@ -332,7 +369,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { runtimeType, const DeepCollectionEquality().hash(error), const DeepCollectionEquality().hash(actionError), - filePath, + profilePath, + bannerPath, currentUserId, endDate, startDate, @@ -340,7 +378,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { loading, enableButton, imageUploading, - imageUrl, + profileImgUrl, + bannerImgUrl, nameController, selectedType); @@ -358,7 +397,8 @@ abstract class _AddTournamentState implements AddTournamentState { const factory _AddTournamentState( {final Object? error, final Object? actionError, - final String? filePath, + final String? profilePath, + final String? bannerPath, final String? currentUserId, required final DateTime endDate, required final DateTime startDate, @@ -366,7 +406,8 @@ abstract class _AddTournamentState implements AddTournamentState { final bool loading, final bool enableButton, final bool imageUploading, - final String? imageUrl, + final String? profileImgUrl, + final String? bannerImgUrl, required final TextEditingController nameController, final TournamentType selectedType}) = _$AddTournamentStateImpl; @@ -375,7 +416,9 @@ abstract class _AddTournamentState implements AddTournamentState { @override Object? get actionError; @override - String? get filePath; + String? get profilePath; + @override + String? get bannerPath; @override String? get currentUserId; @override @@ -391,7 +434,9 @@ abstract class _AddTournamentState implements AddTournamentState { @override bool get imageUploading; @override - String? get imageUrl; + String? get profileImgUrl; + @override + String? get bannerImgUrl; @override TextEditingController get nameController; @override From b873526d772ceb942c86524173496fc195c379cd Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Thu, 10 Oct 2024 13:52:40 +0530 Subject: [PATCH 13/33] minor changes --- .../tournament/tournament_service.dart | 23 +- khelo/assets/locales/app_en.arb | 1 + .../ui/flow/my_game/my_game_tab_screen.dart | 117 +++++----- .../tournament/add/add_tournament_screen.dart | 200 +++++++++--------- .../add/add_tournament_view_model.dart | 27 ++- .../add_tournament_view_model.freezed.dart | 25 ++- .../tournament/tournament_list_screen.dart | 8 - .../tournament_list_view_model.dart | 50 +++-- .../tournament_list_view_model.freezed.dart | 32 ++- 9 files changed, 273 insertions(+), 210 deletions(-) diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index ef904ffe..093b66ff 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -3,23 +3,15 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../api/tournament/tournament_model.dart'; import '../../errors/app_error.dart'; -import '../../storage/app_preferences.dart'; import '../../utils/constant/firestore_constant.dart'; -final tournamentServiceProvider = Provider((ref) { - final service = TournamentService( - FirebaseFirestore.instance, - ref.read(currentUserPod)?.id, - ); - ref.listen(currentUserPod, (_, next) => service._currentUserId = next?.id); - return service; -}); +final tournamentServiceProvider = + Provider((ref) => TournamentService(FirebaseFirestore.instance)); class TournamentService { - String? _currentUserId; final FirebaseFirestore _firestore; - TournamentService(this._firestore, this._currentUserId); + TournamentService(this._firestore); CollectionReference get _tournamentCollection => _firestore.collection(FireStoreConst.tournamentCollection).withConverter( @@ -39,18 +31,15 @@ class TournamentService { } } - Stream> streamCurrentUserRelatedMatches() { - if (_currentUserId == null) { - return Stream.value([]); - } - final currentMember = TournamentMember(id: _currentUserId ?? 'INVALID ID'); + Stream> streamCurrentUserRelatedMatches(String userId) { + final currentMember = TournamentMember(id: userId); final memberContains = [ currentMember.copyWith(role: TournamentMemberRole.organizer).toJson(), currentMember.copyWith(role: TournamentMemberRole.admin).toJson(), ]; final filter = Filter.or( - Filter(FireStoreConst.createdBy, isEqualTo: _currentUserId), + Filter(FireStoreConst.createdBy, isEqualTo: userId), Filter(FireStoreConst.members, arrayContainsAny: memberContains), ); diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2ce79083..40b5e33b 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -153,6 +153,7 @@ "add_tournament_end_date": "End Date", "add_tournament_edit_banner": "Edit banner", "add_tournament_add_banner_placeholder": "Add banner image", + "add_tournament_date_error": "End date should be greater than start date", "tournament_list_empty_list_title": "No tournaments created", "tournament_list_empty_list_description": "Tap on the “ + ” icon to create a tournament", diff --git a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart index 32c9bb69..a3caf408 100644 --- a/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart +++ b/khelo/lib/ui/flow/my_game/my_game_tab_screen.dart @@ -91,60 +91,75 @@ class _MyGameTabScreenState extends ConsumerState padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ - const SizedBox(width: 16), - TabButton( - context.l10n.common_matches_title, - selected: _selectedTab == 0, - onTap: () { - _controller.jumpToPage(0); - }, - ), - const SizedBox(width: 8), - TabButton( - context.l10n.my_cricket_teams_tab_title, - selected: _selectedTab == 1, - onTap: () { - _controller.jumpToPage(1); - }, - ), - const SizedBox(width: 8), - TabButton( - context.l10n.my_cricket_tournament_title, - selected: _selectedTab == 2, - onTap: () { - _controller.jumpToPage(2); - }, - ), - const Spacer(), - if (_selectedTab == 1 && - ref.watch(teamListViewStateProvider).teams.isNotEmpty) ...[ - actionButton( - context, - onPressed: () => ref - .read(teamListViewStateProvider.notifier) - .onFilterButtonTap(), - icon: Icon( - CupertinoIcons.slider_horizontal_3, - color: context.colorScheme.textPrimary, + Expanded( + child: SizedBox( + height: 36, + child: ListView( + scrollDirection: Axis.horizontal, + children: [ + const SizedBox(width: 16), + TabButton( + context.l10n.common_matches_title, + selected: _selectedTab == 0, + onTap: () { + _controller.jumpToPage(0); + }, + ), + const SizedBox(width: 8), + TabButton( + context.l10n.my_cricket_teams_tab_title, + selected: _selectedTab == 1, + onTap: () { + _controller.jumpToPage(1); + }, + ), + const SizedBox(width: 8), + TabButton( + context.l10n.my_cricket_tournament_title, + selected: _selectedTab == 2, + onTap: () { + _controller.jumpToPage(2); + }, + ), + const SizedBox(width: 16), + ], ), - shrinkWrap: true, ), - ], - actionButton( - context, - onPressed: () { - final actions = [ - AppRoute.addMatch(), - AppRoute.addTeam(), - AppRoute.addTournament(), - ]; - actions[_selectedTab].push(context); - }, - icon: Icon( - Icons.add, - color: context.colorScheme.textPrimary, + ), + Container( + color: context.colorScheme.surface, + child: Row( + children: [ + if (_selectedTab == 1 && + ref.watch(teamListViewStateProvider).teams.isNotEmpty) ...[ + actionButton( + context, + onPressed: () => ref + .read(teamListViewStateProvider.notifier) + .onFilterButtonTap(), + icon: Icon( + CupertinoIcons.slider_horizontal_3, + color: context.colorScheme.textPrimary, + ), + ), + ], + actionButton( + context, + onPressed: () { + final actions = [ + AppRoute.addMatch(), + AppRoute.addTeam(), + AppRoute.addTournament(), + ]; + actions[_selectedTab].push(context); + }, + icon: Icon( + Icons.add, + color: context.colorScheme.textPrimary, + ), + ), + ], ), - shrinkWrap: true, ), ], ), diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index cc942bcb..b0e1d184 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -62,18 +62,28 @@ class _AddTournamentScreenState extends ConsumerState { }); } + void _observeDateError(BuildContext context, WidgetRef ref) { + ref.listen( + addTournamentStateProvider.select((value) => value.showDateError), + (previous, next) { + showErrorSnackBar( + context: context, error: context.l10n.add_tournament_date_error); + }); + } + @override Widget build(BuildContext context) { _observeActionError(context, ref); + _observeDateError(context, ref); _observePop(context, ref); final state = ref.watch(addTournamentStateProvider); return AppPage( + title: context.l10n.add_tournament_screen_title, body: Builder( builder: (context) => Stack( children: [ - _bannerView(context, state), _body(context, state), _stickyButton(context, state), ], @@ -83,107 +93,89 @@ class _AddTournamentScreenState extends ConsumerState { } Widget _body(BuildContext context, AddTournamentState state) { - return Padding( - padding: EdgeInsets.all(16) - .copyWith(top: context.mediaQuerySize.height * 0.22), - child: SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _profileView(context, state), - const SizedBox(height: 16), - AppTextField( - controller: state.nameController, - label: context.l10n.add_tournament_name, - onChanged: (_) => notifier.onChange(), - contentPadding: - const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - borderRadius: BorderRadius.circular(12), - borderType: AppTextFieldBorderType.outline, - borderColor: BorderColor( - focusColor: context.colorScheme.outline, - unFocusColor: context.colorScheme.outline, + return ListView( + padding: context.mediaQueryPadding + BottomStickyOverlay.padding, + children: [ + _bannerView(context, state), + Padding( + padding: const EdgeInsets.all(16).copyWith(top: 16), + child: Column( + children: [ + AppTextField( + controller: state.nameController, + label: context.l10n.add_tournament_name, + onChanged: (_) => notifier.onChange(), + contentPadding: + const EdgeInsets.symmetric(horizontal: 16, vertical: 12), + borderRadius: BorderRadius.circular(12), + borderType: AppTextFieldBorderType.outline, + borderColor: BorderColor( + focusColor: context.colorScheme.outline, + unFocusColor: context.colorScheme.outline, + ), ), - ), - const SizedBox(height: 16), - AdaptiveOutlinedTile( - placeholder: context.l10n.add_tournament_type_placeholder, - headerText: context.l10n.add_tournament_type, - title: state.selectedType.getString(context), - showTrailingIcon: true, - onTap: () { - _selectTypeSheet( - context, - selectedType: state.selectedType, - onSelected: (type) => notifier.onSelectType(type), - ); - }, - ), - const SizedBox(height: 16), - _dateScheduleView(context, state) - ], + const SizedBox(height: 16), + AdaptiveOutlinedTile( + placeholder: context.l10n.add_tournament_type_placeholder, + headerText: context.l10n.add_tournament_type, + title: state.selectedType.getString(context), + showTrailingIcon: true, + onTap: () { + _selectTypeSheet( + context, + selectedType: state.selectedType, + onSelected: (type) => notifier.onSelectType(type), + ); + }, + ), + const SizedBox(height: 16), + _dateScheduleView(context, state) + ], + ), ), - ), + ], ); } Widget _bannerView(BuildContext context, AddTournamentState state) { - final double bannerHeight = context.mediaQuerySize.height * 0.27; final bool hasBannerImage = state.bannerPath != null || state.bannerImgUrl != null; - return Container( - height: bannerHeight, - width: double.infinity, - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - image: state.bannerPath != null - ? DecorationImage( - image: FileImage(File(state.bannerPath!)), - fit: BoxFit.cover, - ) - : null, - ), - child: Stack( - alignment: Alignment.center, - children: [ - if (state.imageUploading) Center(child: AppProgressIndicator()), - if (!state.imageUploading) - hasBannerImage - ? _buildCachedNetworkOrFileImage(context, state) - : _bannerPlaceholder(context, - onTap: () => _pickImage(isBanner: true)), - if (hasBannerImage && !state.imageUploading) - Positioned( - bottom: 16, - right: 16, - child: _editBannerButton(context), - ), - _buildHeader(context), - ], - ), - ); - } - - Widget _buildHeader(BuildContext context) { - return Positioned( - top: 16, - left: 0, - child: Padding( - padding: context.mediaQueryPadding, - child: Row( - children: [ - BackButton(color: context.colorScheme.textPrimary), - const SizedBox(width: 16), - Text( - context.l10n.add_tournament_screen_title, - style: AppTextStyle.header2.copyWith( - color: context.colorScheme.textPrimary, - ), - ) - ], + return Stack( + children: [ + Container( + height: 204, + width: context.mediaQuerySize.width, + margin: EdgeInsets.only(bottom: 45), + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + image: state.bannerPath != null + ? DecorationImage( + image: FileImage(File(state.bannerPath!)), + fit: BoxFit.cover, + ) + : null, + ), + child: Stack( + alignment: Alignment.center, + children: [ + if (state.imageUploading) Center(child: AppProgressIndicator()), + if (!state.imageUploading) + hasBannerImage + ? _buildCachedNetworkOrFileImage(context, state) + : _bannerPlaceholder(context, + onTap: () => _pickImage(isBanner: true)), + if (hasBannerImage && !state.imageUploading) + Positioned( + bottom: 16, + right: 16, + child: _editBannerButton(context), + ), + ], + ), ), - ), + _profileView(context, state), + ], ); } @@ -275,14 +267,18 @@ class _AddTournamentScreenState extends ConsumerState { } Widget _profileView(BuildContext context, AddTournamentState state) { - return ProfileImageAvatar( - size: 90, - isLoading: state.imageUploading && state.profilePath == null, - filePath: state.profilePath, - imageUrl: state.profileImgUrl, - placeHolderImage: Assets.images.icTournaments, - alignment: Alignment.centerLeft, - onEditButtonTap: () => _pickImage(), + return Positioned( + left: 16, + bottom: 0, + child: ProfileImageAvatar( + size: 90, + isLoading: state.imageUploading && state.profilePath == null, + filePath: state.profilePath, + imageUrl: state.profileImgUrl, + placeHolderImage: Assets.images.icTournaments, + alignment: Alignment.centerLeft, + onEditButtonTap: _pickImage, + ), ); } @@ -304,7 +300,7 @@ class _AddTournamentScreenState extends ConsumerState { selectDate( context, initialDate: state.startDate, - onDateSelected: (date) => notifier.onStartDate(date), + onDateSelected: notifier.onStartDate, ); }), ), @@ -318,7 +314,7 @@ class _AddTournamentScreenState extends ConsumerState { selectDate( context, initialDate: state.endDate, - onDateSelected: (date) => notifier.onEndDate(date), + onDateSelected: notifier.onEndDate, ); }), ), diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index f4ff7d50..f222fdc7 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -90,7 +90,12 @@ class AddTournamentViewNotifier extends StateNotifier { try { if (state.currentUserId == null) return; - state = state.copyWith(loading: true); + if (state.endDate.isBefore(state.startDate)) { + state = state.copyWith(showDateError: true); + return; + } + + state = state.copyWith(loading: true, showDateError: false); final tournamentId = _tournamentService.generateTournamentId; final name = state.nameController.text.trim(); @@ -98,8 +103,7 @@ class AddTournamentViewNotifier extends StateNotifier { final profileImgUrl = await _fileUploadService.uploadProfileImage( filePath: state.profilePath ?? '', uploadPath: StorageConst.tournamentProfileUploadPath( - userId: state.currentUserId ?? 'INVALID ID', - tournamentId: tournamentId), + userId: state.currentUserId!, tournamentId: tournamentId), ); state = state.copyWith(profileImgUrl: profileImgUrl); } @@ -108,8 +112,7 @@ class AddTournamentViewNotifier extends StateNotifier { final bannerImgUrl = await _fileUploadService.uploadProfileImage( filePath: state.bannerPath ?? '', uploadPath: StorageConst.tournamentBannerUploadPath( - userId: state.currentUserId ?? 'INVALID ID', - tournamentId: tournamentId), + userId: state.currentUserId!, tournamentId: tournamentId), ); state = state.copyWith(bannerImgUrl: bannerImgUrl); } @@ -121,10 +124,10 @@ class AddTournamentViewNotifier extends StateNotifier { start_date: state.startDate, end_date: state.endDate, created_at: DateTime.now(), - created_by: state.currentUserId ?? 'INVALID ID', + created_by: state.currentUserId!, members: [ TournamentMember( - id: state.currentUserId ?? 'INVALID ID', + id: state.currentUserId!, role: TournamentMemberRole.organizer, ), ], @@ -134,13 +137,20 @@ class AddTournamentViewNotifier extends StateNotifier { await _tournamentService.createTournament(tournament); - state = state.copyWith(pop: true, loading: false, error: null); + state = state.copyWith( + pop: true, loading: false, error: null, showDateError: false); } catch (error) { state = state.copyWith(loading: false, error: error); debugPrint( "AddTournamentViewNotifier: error while adding tournament -> $error"); } } + + @override + void dispose() { + state.nameController.dispose(); + super.dispose(); + } } @freezed @@ -155,6 +165,7 @@ class AddTournamentState with _$AddTournamentState { required DateTime startDate, @Default(false) bool pop, @Default(false) bool loading, + @Default(false) bool showDateError, @Default(false) bool enableButton, @Default(false) bool imageUploading, @Default(null) String? profileImgUrl, diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart index 23f8cfae..a55c612f 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -25,6 +25,7 @@ mixin _$AddTournamentState { DateTime get startDate => throw _privateConstructorUsedError; bool get pop => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; + bool get showDateError => throw _privateConstructorUsedError; bool get enableButton => throw _privateConstructorUsedError; bool get imageUploading => throw _privateConstructorUsedError; String? get profileImgUrl => throw _privateConstructorUsedError; @@ -56,6 +57,7 @@ abstract class $AddTournamentStateCopyWith<$Res> { DateTime startDate, bool pop, bool loading, + bool showDateError, bool enableButton, bool imageUploading, String? profileImgUrl, @@ -88,6 +90,7 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> Object? startDate = null, Object? pop = null, Object? loading = null, + Object? showDateError = null, Object? enableButton = null, Object? imageUploading = null, Object? profileImgUrl = freezed, @@ -126,6 +129,10 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + showDateError: null == showDateError + ? _value.showDateError + : showDateError // ignore: cast_nullable_to_non_nullable + as bool, enableButton: null == enableButton ? _value.enableButton : enableButton // ignore: cast_nullable_to_non_nullable @@ -172,6 +179,7 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> DateTime startDate, bool pop, bool loading, + bool showDateError, bool enableButton, bool imageUploading, String? profileImgUrl, @@ -202,6 +210,7 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> Object? startDate = null, Object? pop = null, Object? loading = null, + Object? showDateError = null, Object? enableButton = null, Object? imageUploading = null, Object? profileImgUrl = freezed, @@ -240,6 +249,10 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + showDateError: null == showDateError + ? _value.showDateError + : showDateError // ignore: cast_nullable_to_non_nullable + as bool, enableButton: null == enableButton ? _value.enableButton : enableButton // ignore: cast_nullable_to_non_nullable @@ -281,6 +294,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { required this.startDate, this.pop = false, this.loading = false, + this.showDateError = false, this.enableButton = false, this.imageUploading = false, this.profileImgUrl = null, @@ -310,6 +324,9 @@ class _$AddTournamentStateImpl implements _AddTournamentState { final bool loading; @override @JsonKey() + final bool showDateError; + @override + @JsonKey() final bool enableButton; @override @JsonKey() @@ -328,7 +345,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override String toString() { - return 'AddTournamentState(error: $error, actionError: $actionError, profilePath: $profilePath, bannerPath: $bannerPath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, enableButton: $enableButton, imageUploading: $imageUploading, profileImgUrl: $profileImgUrl, bannerImgUrl: $bannerImgUrl, nameController: $nameController, selectedType: $selectedType)'; + return 'AddTournamentState(error: $error, actionError: $actionError, profilePath: $profilePath, bannerPath: $bannerPath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, showDateError: $showDateError, enableButton: $enableButton, imageUploading: $imageUploading, profileImgUrl: $profileImgUrl, bannerImgUrl: $bannerImgUrl, nameController: $nameController, selectedType: $selectedType)'; } @override @@ -350,6 +367,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { other.startDate == startDate) && (identical(other.pop, pop) || other.pop == pop) && (identical(other.loading, loading) || other.loading == loading) && + (identical(other.showDateError, showDateError) || + other.showDateError == showDateError) && (identical(other.enableButton, enableButton) || other.enableButton == enableButton) && (identical(other.imageUploading, imageUploading) || @@ -376,6 +395,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { startDate, pop, loading, + showDateError, enableButton, imageUploading, profileImgUrl, @@ -404,6 +424,7 @@ abstract class _AddTournamentState implements AddTournamentState { required final DateTime startDate, final bool pop, final bool loading, + final bool showDateError, final bool enableButton, final bool imageUploading, final String? profileImgUrl, @@ -430,6 +451,8 @@ abstract class _AddTournamentState implements AddTournamentState { @override bool get loading; @override + bool get showDateError; + @override bool get enableButton; @override bool get imageUploading; diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index 5470bb58..7c40e06a 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -80,14 +80,6 @@ class _TournamentListScreenState extends ConsumerState return CustomScrollView( slivers: [ ..._tournaments(context, state), - SliverToBoxAdapter( - child: state.groupTournaments.isNotEmpty && state.loading - ? const Padding( - padding: EdgeInsets.all(16), - child: Center(child: AppProgressIndicator()), - ) - : const SizedBox(), - ), SliverToBoxAdapter( child: SizedBox(height: 16 + context.mediaQueryPadding.bottom), ), diff --git a/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart index 45606b32..5b04c615 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_view_model.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:collection/collection.dart'; import 'package:data/api/tournament/tournament_model.dart'; import 'package:data/service/tournament/tournament_service.dart'; +import 'package:data/storage/app_preferences.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -11,7 +12,16 @@ part 'tournament_list_view_model.freezed.dart'; final tournamentListViewStateProvider = StateNotifierProvider( - (ref) => TournamentListViewNotifier(ref.read(tournamentServiceProvider)), + (ref) { + final notifier = TournamentListViewNotifier( + ref.read(tournamentServiceProvider), + ref.read(currentUserPod)?.id, + ); + ref.listen(currentUserPod, (previous, next) { + notifier._setUserId(next?.id); + }); + return notifier; + }, ); class TournamentListViewNotifier @@ -19,31 +29,34 @@ class TournamentListViewNotifier final TournamentService _tournamentService; StreamSubscription? _tournamentStreamSubscription; - TournamentListViewNotifier(this._tournamentService) - : super(const TournamentListViewState()) { + TournamentListViewNotifier(this._tournamentService, String? userId) + : super(TournamentListViewState(currentUserId: userId)) { + loadTournaments(); + } + + void _setUserId(String? userId) { + if (userId == null) { + _tournamentStreamSubscription?.cancel(); + } + state = state.copyWith(currentUserId: userId); loadTournaments(); } Future loadTournaments() async { + if (state.currentUserId == null) return; _tournamentStreamSubscription?.cancel(); - state = state.copyWith(loading: state.groupTournaments.isEmpty); - try { - _tournamentStreamSubscription = _tournamentService - .streamCurrentUserRelatedMatches() - .listen((tournaments) { - final groupTournaments = _groupTournaments(tournaments); - state = state.copyWith( - groupTournaments: groupTournaments, loading: false, error: null); - }, onError: (e) { - state = state.copyWith(loading: false, error: e); - debugPrint( - "TournamentListViewNotifier: error while loading tournament list -> $e"); - }); - } catch (e) { + state = state.copyWith(loading: true); + _tournamentStreamSubscription = _tournamentService + .streamCurrentUserRelatedMatches(state.currentUserId!) + .listen((tournaments) { + final groupTournaments = _groupTournaments(tournaments); + state = state.copyWith( + groupTournaments: groupTournaments, loading: false, error: null); + }, onError: (e) { state = state.copyWith(loading: false, error: e); debugPrint( "TournamentListViewNotifier: error while loading tournament list -> $e"); - } + }); } Map> _groupTournaments( @@ -64,6 +77,7 @@ class TournamentListViewNotifier @freezed class TournamentListViewState with _$TournamentListViewState { const factory TournamentListViewState({ + String? currentUserId, Object? error, @Default(true) bool loading, @Default({}) Map> groupTournaments, diff --git a/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart index 297993b6..df63d56a 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_view_model.freezed.dart @@ -16,6 +16,7 @@ final _privateConstructorUsedError = UnsupportedError( /// @nodoc mixin _$TournamentListViewState { + String? get currentUserId => throw _privateConstructorUsedError; Object? get error => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; Map> get groupTournaments => @@ -35,7 +36,8 @@ abstract class $TournamentListViewStateCopyWith<$Res> { _$TournamentListViewStateCopyWithImpl<$Res, TournamentListViewState>; @useResult $Res call( - {Object? error, + {String? currentUserId, + Object? error, bool loading, Map> groupTournaments}); } @@ -56,11 +58,16 @@ class _$TournamentListViewStateCopyWithImpl<$Res, @pragma('vm:prefer-inline') @override $Res call({ + Object? currentUserId = freezed, Object? error = freezed, Object? loading = null, Object? groupTournaments = null, }) { return _then(_value.copyWith( + currentUserId: freezed == currentUserId + ? _value.currentUserId + : currentUserId // ignore: cast_nullable_to_non_nullable + as String?, error: freezed == error ? _value.error : error, loading: null == loading ? _value.loading @@ -84,7 +91,8 @@ abstract class _$$TournamentListViewStateImplCopyWith<$Res> @override @useResult $Res call( - {Object? error, + {String? currentUserId, + Object? error, bool loading, Map> groupTournaments}); } @@ -104,11 +112,16 @@ class __$$TournamentListViewStateImplCopyWithImpl<$Res> @pragma('vm:prefer-inline') @override $Res call({ + Object? currentUserId = freezed, Object? error = freezed, Object? loading = null, Object? groupTournaments = null, }) { return _then(_$TournamentListViewStateImpl( + currentUserId: freezed == currentUserId + ? _value.currentUserId + : currentUserId // ignore: cast_nullable_to_non_nullable + as String?, error: freezed == error ? _value.error : error, loading: null == loading ? _value.loading @@ -126,11 +139,14 @@ class __$$TournamentListViewStateImplCopyWithImpl<$Res> class _$TournamentListViewStateImpl implements _TournamentListViewState { const _$TournamentListViewStateImpl( - {this.error, + {this.currentUserId, + this.error, this.loading = true, final Map> groupTournaments = const {}}) : _groupTournaments = groupTournaments; + @override + final String? currentUserId; @override final Object? error; @override @@ -147,7 +163,7 @@ class _$TournamentListViewStateImpl implements _TournamentListViewState { @override String toString() { - return 'TournamentListViewState(error: $error, loading: $loading, groupTournaments: $groupTournaments)'; + return 'TournamentListViewState(currentUserId: $currentUserId, error: $error, loading: $loading, groupTournaments: $groupTournaments)'; } @override @@ -155,6 +171,8 @@ class _$TournamentListViewStateImpl implements _TournamentListViewState { return identical(this, other) || (other.runtimeType == runtimeType && other is _$TournamentListViewStateImpl && + (identical(other.currentUserId, currentUserId) || + other.currentUserId == currentUserId) && const DeepCollectionEquality().equals(other.error, error) && (identical(other.loading, loading) || other.loading == loading) && const DeepCollectionEquality() @@ -164,6 +182,7 @@ class _$TournamentListViewStateImpl implements _TournamentListViewState { @override int get hashCode => Object.hash( runtimeType, + currentUserId, const DeepCollectionEquality().hash(error), loading, const DeepCollectionEquality().hash(_groupTournaments)); @@ -180,11 +199,14 @@ class _$TournamentListViewStateImpl implements _TournamentListViewState { abstract class _TournamentListViewState implements TournamentListViewState { const factory _TournamentListViewState( - {final Object? error, + {final String? currentUserId, + final Object? error, final bool loading, final Map> groupTournaments}) = _$TournamentListViewStateImpl; + @override + String? get currentUserId; @override Object? get error; @override From fde8eeb0102057f38ad0061877f56c1b6d5ca0a8 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Thu, 10 Oct 2024 14:43:32 +0530 Subject: [PATCH 14/33] minor changes --- .../lib/ui/flow/tournament/add/add_tournament_view_model.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index f222fdc7..eaf67e47 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -89,13 +89,13 @@ class AddTournamentViewNotifier extends StateNotifier { void addTournament() async { try { if (state.currentUserId == null) return; + state = state.copyWith(loading: true, showDateError: false); if (state.endDate.isBefore(state.startDate)) { - state = state.copyWith(showDateError: true); + state = state.copyWith(showDateError: true, loading: false); return; } - state = state.copyWith(loading: true, showDateError: false); final tournamentId = _tournamentService.generateTournamentId; final name = state.nameController.text.trim(); From 007d85b1d2fc17d7c91ab788a21afd2c41fec216 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 11 Oct 2024 15:46:07 +0530 Subject: [PATCH 15/33] implement tournament detail --- data/lib/api/tournament/tournament_model.dart | 4 + .../tournament/tournament_model.freezed.dart | 58 ++++- data/lib/service/match/match_service.dart | 14 ++ data/lib/service/team/team_service.dart | 12 ++ .../tournament/tournament_service.dart | 72 ++++++- khelo/assets/images/ic_location.svg | 6 +- khelo/assets/locales/app_en.arb | 14 ++ khelo/lib/components/image_avatar.dart | 3 + khelo/lib/ui/app_route.dart | 11 + .../detail/tournament_detail_screen.dart | 176 +++++++++++++++ .../detail/tournament_detail_view_model.dart | 57 +++++ .../tournament_detail_view_model.freezed.dart | 200 ++++++++++++++++++ .../tournament/tournament_list_screen.dart | 4 +- 13 files changed, 616 insertions(+), 15 deletions(-) create mode 100644 khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart create mode 100644 khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart create mode 100644 khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index 07b9c4c7..568291b2 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -6,6 +6,7 @@ import 'package:freezed_annotation/freezed_annotation.dart'; import '../../converter/timestamp_json_converter.dart'; import '../match/match_model.dart'; import '../team/team_model.dart'; +import '../user/user_models.dart'; part 'tournament_model.freezed.dart'; @@ -47,8 +48,11 @@ class TournamentModel with _$TournamentModel { @freezed class TournamentMember with _$TournamentMember { + @JsonSerializable(explicitToJson: true) const factory TournamentMember({ required String id, + @JsonKey(includeToJson: false, includeFromJson: false) + @Default(UserModel(id: '')) UserModel user, @Default(TournamentMemberRole.admin) TournamentMemberRole role, }) = _TournamentMember; diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index 9eba15b3..ed37d162 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -518,6 +518,8 @@ TournamentMember _$TournamentMemberFromJson(Map json) { /// @nodoc mixin _$TournamentMember { String get id => throw _privateConstructorUsedError; + @JsonKey(includeToJson: false, includeFromJson: false) + UserModel get user => throw _privateConstructorUsedError; TournamentMemberRole get role => throw _privateConstructorUsedError; /// Serializes this TournamentMember to a JSON map. @@ -536,7 +538,12 @@ abstract class $TournamentMemberCopyWith<$Res> { TournamentMember value, $Res Function(TournamentMember) then) = _$TournamentMemberCopyWithImpl<$Res, TournamentMember>; @useResult - $Res call({String id, TournamentMemberRole role}); + $Res call( + {String id, + @JsonKey(includeToJson: false, includeFromJson: false) UserModel user, + TournamentMemberRole role}); + + $UserModelCopyWith<$Res> get user; } /// @nodoc @@ -555,6 +562,7 @@ class _$TournamentMemberCopyWithImpl<$Res, $Val extends TournamentMember> @override $Res call({ Object? id = null, + Object? user = null, Object? role = null, }) { return _then(_value.copyWith( @@ -562,12 +570,26 @@ class _$TournamentMemberCopyWithImpl<$Res, $Val extends TournamentMember> ? _value.id : id // ignore: cast_nullable_to_non_nullable as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserModel, role: null == role ? _value.role : role // ignore: cast_nullable_to_non_nullable as TournamentMemberRole, ) as $Val); } + + /// Create a copy of TournamentMember + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserModelCopyWith<$Res> get user { + return $UserModelCopyWith<$Res>(_value.user, (value) { + return _then(_value.copyWith(user: value) as $Val); + }); + } } /// @nodoc @@ -578,7 +600,13 @@ abstract class _$$TournamentMemberImplCopyWith<$Res> __$$TournamentMemberImplCopyWithImpl<$Res>; @override @useResult - $Res call({String id, TournamentMemberRole role}); + $Res call( + {String id, + @JsonKey(includeToJson: false, includeFromJson: false) UserModel user, + TournamentMemberRole role}); + + @override + $UserModelCopyWith<$Res> get user; } /// @nodoc @@ -595,6 +623,7 @@ class __$$TournamentMemberImplCopyWithImpl<$Res> @override $Res call({ Object? id = null, + Object? user = null, Object? role = null, }) { return _then(_$TournamentMemberImpl( @@ -602,6 +631,10 @@ class __$$TournamentMemberImplCopyWithImpl<$Res> ? _value.id : id // ignore: cast_nullable_to_non_nullable as String, + user: null == user + ? _value.user + : user // ignore: cast_nullable_to_non_nullable + as UserModel, role: null == role ? _value.role : role // ignore: cast_nullable_to_non_nullable @@ -611,10 +644,14 @@ class __$$TournamentMemberImplCopyWithImpl<$Res> } /// @nodoc -@JsonSerializable() + +@JsonSerializable(explicitToJson: true) class _$TournamentMemberImpl implements _TournamentMember { const _$TournamentMemberImpl( - {required this.id, this.role = TournamentMemberRole.admin}); + {required this.id, + @JsonKey(includeToJson: false, includeFromJson: false) + this.user = const UserModel(id: ''), + this.role = TournamentMemberRole.admin}); factory _$TournamentMemberImpl.fromJson(Map json) => _$$TournamentMemberImplFromJson(json); @@ -622,12 +659,15 @@ class _$TournamentMemberImpl implements _TournamentMember { @override final String id; @override + @JsonKey(includeToJson: false, includeFromJson: false) + final UserModel user; + @override @JsonKey() final TournamentMemberRole role; @override String toString() { - return 'TournamentMember(id: $id, role: $role)'; + return 'TournamentMember(id: $id, user: $user, role: $role)'; } @override @@ -636,12 +676,13 @@ class _$TournamentMemberImpl implements _TournamentMember { (other.runtimeType == runtimeType && other is _$TournamentMemberImpl && (identical(other.id, id) || other.id == id) && + (identical(other.user, user) || other.user == user) && (identical(other.role, role) || other.role == role)); } @JsonKey(includeFromJson: false, includeToJson: false) @override - int get hashCode => Object.hash(runtimeType, id, role); + int get hashCode => Object.hash(runtimeType, id, user, role); /// Create a copy of TournamentMember /// with the given fields replaced by the non-null parameter values. @@ -663,6 +704,8 @@ class _$TournamentMemberImpl implements _TournamentMember { abstract class _TournamentMember implements TournamentMember { const factory _TournamentMember( {required final String id, + @JsonKey(includeToJson: false, includeFromJson: false) + final UserModel user, final TournamentMemberRole role}) = _$TournamentMemberImpl; factory _TournamentMember.fromJson(Map json) = @@ -671,6 +714,9 @@ abstract class _TournamentMember implements TournamentMember { @override String get id; @override + @JsonKey(includeToJson: false, includeFromJson: false) + UserModel get user; + @override TournamentMemberRole get role; /// Create a copy of TournamentMember diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index a16cf454..cbd227ef 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -464,4 +464,18 @@ class MatchService { throw AppError.fromError(error, stack); } } + + //Helper Methods + Future> getMatchesByIds(List matchIds) async { + try { + final List matches = []; + await Future.forEach(matchIds, (matchId) async { + final match = await getMatchById(matchId); + matches.add(match); + }); + return matches; + } catch (error, stack) { + throw AppError.fromError(error, stack); + } + } } diff --git a/data/lib/service/team/team_service.dart b/data/lib/service/team/team_service.dart index 4460b7ce..1d119eaf 100644 --- a/data/lib/service/team/team_service.dart +++ b/data/lib/service/team/team_service.dart @@ -267,6 +267,18 @@ class TeamService { } } + Future> getTeamsByIds(List teamIds) async { + try { + final teamList = await _teamsCollection + .where(FieldPath.documentId, whereIn: teamIds) + .get() + .then((value) => value.docs.map((e) => e.data()).toList()); + return teamList; + } catch (error, stack) { + throw AppError.fromError(error, stack); + } + } + Stream> streamUserRelatedTeamsByUserId(String userId) { final currentPlayer = TeamPlayer(id: userId); diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index 093b66ff..55bdfab9 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -4,17 +4,34 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../api/tournament/tournament_model.dart'; import '../../errors/app_error.dart'; import '../../utils/constant/firestore_constant.dart'; +import '../match/match_service.dart'; +import '../team/team_service.dart'; +import '../user/user_service.dart'; -final tournamentServiceProvider = - Provider((ref) => TournamentService(FirebaseFirestore.instance)); +final tournamentServiceProvider = Provider( + (ref) => TournamentService( + fireStore: FirebaseFirestore.instance, + teamService: ref.read(teamServiceProvider), + matchService: ref.read(matchServiceProvider), + userService: ref.read(userServiceProvider), + ), +); class TournamentService { - final FirebaseFirestore _firestore; + final FirebaseFirestore fireStore; + final TeamService teamService; + final MatchService matchService; + final UserService userService; - TournamentService(this._firestore); + TournamentService({ + required this.fireStore, + required this.teamService, + required this.matchService, + required this.userService, + }); CollectionReference get _tournamentCollection => - _firestore.collection(FireStoreConst.tournamentCollection).withConverter( + fireStore.collection(FireStoreConst.tournamentCollection).withConverter( fromFirestore: TournamentModel.fromFireStore, toFirestore: (TournamentModel tournament, _) => tournament.toJson(), ); @@ -49,4 +66,49 @@ class TournamentService { .map((event) => event.docs.map((e) => e.data()).toList()) .handleError((error, stack) => throw AppError.fromError(error, stack)); } + + Stream streamTournamentById(String tournamentId) { + return _tournamentCollection + .doc(tournamentId) + .snapshots() + .asyncMap((snapshot) async { + if (snapshot.data() == null) { + return TournamentModel( + id: tournamentId, + name: '', + created_by: '', + type: TournamentType.other, + start_date: DateTime.now(), + ); + } else { + var tournament = snapshot.data()!; + final teamIds = tournament.team_ids; + final matchIds = tournament.match_ids; + + if (teamIds.isNotEmpty) { + final teams = await teamService.getTeamsByIds(teamIds); + tournament = tournament.copyWith(teams: teams); + } + + if (matchIds.isNotEmpty) { + final matches = await matchService.getMatchesByIds(matchIds); + tournament = tournament.copyWith(matches: matches); + } + + if (tournament.members.isNotEmpty) { + final memberIds = tournament.members.map((e) => e.id).toList(); + final users = await userService.getUsersByIds(memberIds); + + final members = tournament.members.map((member) { + final user = users.firstWhere((element) => element.id == member.id); + return member.copyWith(user: user); + }).toList(); + + tournament = tournament.copyWith(members: members); + } + + return tournament; + } + }).handleError((error, stack) => throw AppError.fromError(error, stack)); + } } diff --git a/khelo/assets/images/ic_location.svg b/khelo/assets/images/ic_location.svg index 39251a6f..a5bb8734 100644 --- a/khelo/assets/images/ic_location.svg +++ b/khelo/assets/images/ic_location.svg @@ -1,4 +1,4 @@ - - - + + + diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 40b5e33b..4506adf3 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -164,6 +164,20 @@ } }, + "@_TOURNAMENT_DETAIL": { + }, + "tournament_detail_not_found_title": "No Tournament found", + "tournament_detail_not_found_description": "The tournament you are looking for does not available.", + "tournament_detail_start_from_title": "Start from {date}", + "@tournament_detail_start_from_title": { + "description": "Start from {date}", + "placeholders": { + "date": { + "type": "String" + } + } + }, + "@_TOURNAMENT_TYPE":{ }, "tournament_type_knock_out": "Knockout", diff --git a/khelo/lib/components/image_avatar.dart b/khelo/lib/components/image_avatar.dart index ef8dbcb7..fce92815 100644 --- a/khelo/lib/components/image_avatar.dart +++ b/khelo/lib/components/image_avatar.dart @@ -8,6 +8,7 @@ class ImageAvatar extends StatelessWidget { final double size; final String? imageUrl; final String initial; + final Border? border; final Color? foregroundColor; final Color? backgroundColor; @@ -15,6 +16,7 @@ class ImageAvatar extends StatelessWidget { super.key, this.size = 50, this.imageUrl, + this.border, required this.initial, this.foregroundColor, this.backgroundColor, @@ -29,6 +31,7 @@ class ImageAvatar extends StatelessWidget { alignment: Alignment.center, decoration: BoxDecoration( shape: BoxShape.circle, + border: border, color: backgroundColor ?? context.colorScheme.containerHigh, ), child: imageUrl == null diff --git a/khelo/lib/ui/app_route.dart b/khelo/lib/ui/app_route.dart index 4b90d3f6..16abb666 100644 --- a/khelo/lib/ui/app_route.dart +++ b/khelo/lib/ui/app_route.dart @@ -32,6 +32,7 @@ import 'flow/main/main_screen.dart'; import 'flow/settings/support/contact_support_screen.dart'; import 'flow/sign_in/sign_in_with_phone/sign_in_with_phone_screen.dart'; import 'flow/team/user_detail/user_detail_screen.dart'; +import 'flow/tournament/detail/tournament_detail_screen.dart'; class AppRoute { static const pathPhoneNumberVerification = '/phone-number-verification'; @@ -55,6 +56,7 @@ class AppRoute { static const pathViewAll = "/view-all"; static const pathContactSelection = "/contact-selection"; static const pathAddTournament = "/add-tournament"; + static const pathTournamentDetail = "/tournament-detail"; final String path; final String? name; @@ -172,6 +174,11 @@ class AppRoute { builder: (_) => const AddTournamentScreen(), ); + static AppRoute tournamentDetail({required String tournamentId}) => AppRoute( + pathTournamentDetail, + builder: (_) => TournamentDetailScreen(tournamentId: tournamentId), + ); + static AppRoute matchDetailTab({required String matchId}) => AppRoute( pathMatchDetailTab, builder: (_) => MatchDetailTabScreen(matchId: matchId), @@ -330,6 +337,10 @@ class AppRoute { path: pathAddTournament, builder: (context, state) => state.widget(context), ), + GoRoute( + path: pathTournamentDetail, + builder: (context, state) => state.widget(context), + ), GoRoute( path: pathMatchDetailTab, builder: (context, state) => state.widget(context), diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart new file mode 100644 index 00000000..4624b9de --- /dev/null +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -0,0 +1,176 @@ +import 'package:cached_network_image/cached_network_image.dart'; +import 'package:data/api/tournament/tournament_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/flutter_svg.dart'; +import 'package:khelo/components/empty_screen.dart'; +import 'package:khelo/components/image_avatar.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; +import 'package:khelo/ui/flow/tournament/detail/tournament_detail_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'; + +import '../../../../components/app_page.dart'; +import '../../../../components/error_screen.dart'; +import '../../../../domain/extensions/widget_extension.dart'; +import '../../../../gen/assets.gen.dart'; + +class TournamentDetailScreen extends ConsumerStatefulWidget { + final String tournamentId; + + const TournamentDetailScreen({ + super.key, + required this.tournamentId, + }); + + @override + ConsumerState createState() => + _TournamentDetailScreenState(); +} + +class _TournamentDetailScreenState + extends ConsumerState { + late TournamentDetailStateViewNotifier notifier; + + @override + void initState() { + super.initState(); + notifier = ref.read(tournamentDetailStateProvider.notifier); + runPostFrame(() => notifier.setData(widget.tournamentId)); + } + + @override + Widget build(BuildContext context) { + final state = ref.watch(tournamentDetailStateProvider); + return AppPage( + body: Builder(builder: (context) { + return _body(context, state); + }), + ); + } + + Widget _body(BuildContext context, TournamentDetailState state) { + if (state.loading) { + return const Center(child: AppProgressIndicator()); + } + if (state.error != null) { + return ErrorScreen( + error: state.error, + onRetryTap: notifier.loadTournament, + ); + } + + if (state.tournament == null) { + return EmptyScreen( + title: context.l10n.tournament_detail_not_found_title, + description: context.l10n.tournament_detail_not_found_description, + isShowButton: false, + ); + } + + return CustomScrollView( + slivers: [ + SliverAppBar( + expandedHeight: 300, + backgroundColor: context.colorScheme.surface, + pinned: true, + flexibleSpace: _flexibleTitle(context, state.tournament!), + actions: [], + ), + SliverToBoxAdapter( + child: SizedBox(height: 16 + context.mediaQueryPadding.bottom), + ), + SliverToBoxAdapter( + child: _content(context, state), + ) + ], + ); + } + + Widget _content(BuildContext context, TournamentDetailState state) { + return Column(); + } + + Widget _flexibleTitle(BuildContext context, TournamentModel tournament) { + return FlexibleSpaceBar( + background: Stack( + children: [ + Container( + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + image: (tournament.banner_img_url != null) + ? DecorationImage( + image: CachedNetworkImageProvider( + tournament.banner_img_url ?? ''), + fit: BoxFit.fill, + ) + : null, + ), + child: (tournament.banner_img_url == null) + ? Center( + child: SvgPicture.asset( + Assets.images.icTournaments, + colorFilter: ColorFilter.mode( + context.colorScheme.textPrimary, + BlendMode.srcIn, + ), + ), + ) + : null, + ), + Positioned( + left: 16, + bottom: 24, + child: _profileView(context, tournament), + ), + ], + ), + ); + } + + Widget _profileView(BuildContext context, TournamentModel tournament) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ImageAvatar( + initial: tournament.name.characters.first.toUpperCase(), + size: 80, + imageUrl: tournament.profile_img_url, + border: Border.all( + color: Colors.white, + width: 1.5, + ), + backgroundColor: context.colorScheme.primary, + ), + const SizedBox(height: 16), + Text( + tournament.name, + style: AppTextStyle.header1.copyWith(color: Colors.white), + ), + const SizedBox(height: 4), + Row( + children: [ + SvgPicture.asset( + Assets.images.icCalendar, + height: 24, + width: 24, + colorFilter: ColorFilter.mode( + Colors.white, + BlendMode.srcIn, + ), + ), + const SizedBox(width: 8), + Text( + context.l10n.tournament_detail_start_from_title(tournament + .start_date + .format(context, DateFormatType.dayMonth)), + style: AppTextStyle.body1.copyWith(color: Colors.white), + ), + ], + ) + ], + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart new file mode 100644 index 00000000..ced65490 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -0,0 +1,57 @@ +import 'dart:async'; + +import 'package:data/api/tournament/tournament_model.dart'; +import 'package:data/service/tournament/tournament_service.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'tournament_detail_view_model.freezed.dart'; + +final tournamentDetailStateProvider = StateNotifierProvider.autoDispose< + TournamentDetailStateViewNotifier, TournamentDetailState>( + (ref) => + TournamentDetailStateViewNotifier(ref.read(tournamentServiceProvider)), +); + +class TournamentDetailStateViewNotifier + extends StateNotifier { + final TournamentService _tournamentService; + StreamSubscription? _tournamentSubscription; + + TournamentDetailStateViewNotifier(this._tournamentService) + : super(const TournamentDetailState()); + + String? _tournamentId; + + void setData(String tournamentId) { + _tournamentId = tournamentId; + loadTournament(); + } + + void loadTournament() async { + if (_tournamentId == null) return; + _tournamentSubscription?.cancel(); + + state = state.copyWith(loading: true); + + _tournamentSubscription = _tournamentService + .streamTournamentById(_tournamentId!) + .listen((tournament) { + state = state.copyWith(tournament: tournament, loading: false); + }, onError: (e) { + state = state.copyWith(error: e, loading: false); + debugPrint( + "TournamentListViewNotifier: error while loading tournament list -> $e"); + }); + } +} + +@freezed +class TournamentDetailState with _$TournamentDetailState { + const factory TournamentDetailState({ + @Default(null) TournamentModel? tournament, + @Default(false) bool loading, + Object? error, + }) = _TournamentDetailState; +} diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart new file mode 100644 index 00000000..2ee503e2 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart @@ -0,0 +1,200 @@ +// 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 'tournament_detail_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 _$TournamentDetailState { + TournamentModel? get tournament => throw _privateConstructorUsedError; + bool get loading => throw _privateConstructorUsedError; + Object? get error => throw _privateConstructorUsedError; + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TournamentDetailStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TournamentDetailStateCopyWith<$Res> { + factory $TournamentDetailStateCopyWith(TournamentDetailState value, + $Res Function(TournamentDetailState) then) = + _$TournamentDetailStateCopyWithImpl<$Res, TournamentDetailState>; + @useResult + $Res call({TournamentModel? tournament, bool loading, Object? error}); + + $TournamentModelCopyWith<$Res>? get tournament; +} + +/// @nodoc +class _$TournamentDetailStateCopyWithImpl<$Res, + $Val extends TournamentDetailState> + implements $TournamentDetailStateCopyWith<$Res> { + _$TournamentDetailStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? tournament = freezed, + Object? loading = null, + Object? error = freezed, + }) { + return _then(_value.copyWith( + tournament: freezed == tournament + ? _value.tournament + : tournament // ignore: cast_nullable_to_non_nullable + as TournamentModel?, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + error: freezed == error ? _value.error : error, + ) as $Val); + } + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $TournamentModelCopyWith<$Res>? get tournament { + if (_value.tournament == null) { + return null; + } + + return $TournamentModelCopyWith<$Res>(_value.tournament!, (value) { + return _then(_value.copyWith(tournament: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$TournamentDetailStateImplCopyWith<$Res> + implements $TournamentDetailStateCopyWith<$Res> { + factory _$$TournamentDetailStateImplCopyWith( + _$TournamentDetailStateImpl value, + $Res Function(_$TournamentDetailStateImpl) then) = + __$$TournamentDetailStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({TournamentModel? tournament, bool loading, Object? error}); + + @override + $TournamentModelCopyWith<$Res>? get tournament; +} + +/// @nodoc +class __$$TournamentDetailStateImplCopyWithImpl<$Res> + extends _$TournamentDetailStateCopyWithImpl<$Res, + _$TournamentDetailStateImpl> + implements _$$TournamentDetailStateImplCopyWith<$Res> { + __$$TournamentDetailStateImplCopyWithImpl(_$TournamentDetailStateImpl _value, + $Res Function(_$TournamentDetailStateImpl) _then) + : super(_value, _then); + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? tournament = freezed, + Object? loading = null, + Object? error = freezed, + }) { + return _then(_$TournamentDetailStateImpl( + tournament: freezed == tournament + ? _value.tournament + : tournament // ignore: cast_nullable_to_non_nullable + as TournamentModel?, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + error: freezed == error ? _value.error : error, + )); + } +} + +/// @nodoc + +class _$TournamentDetailStateImpl implements _TournamentDetailState { + const _$TournamentDetailStateImpl( + {this.tournament = null, this.loading = false, this.error}); + + @override + @JsonKey() + final TournamentModel? tournament; + @override + @JsonKey() + final bool loading; + @override + final Object? error; + + @override + String toString() { + return 'TournamentDetailState(tournament: $tournament, loading: $loading, error: $error)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TournamentDetailStateImpl && + (identical(other.tournament, tournament) || + other.tournament == tournament) && + (identical(other.loading, loading) || other.loading == loading) && + const DeepCollectionEquality().equals(other.error, error)); + } + + @override + int get hashCode => Object.hash(runtimeType, tournament, loading, + const DeepCollectionEquality().hash(error)); + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TournamentDetailStateImplCopyWith<_$TournamentDetailStateImpl> + get copyWith => __$$TournamentDetailStateImplCopyWithImpl< + _$TournamentDetailStateImpl>(this, _$identity); +} + +abstract class _TournamentDetailState implements TournamentDetailState { + const factory _TournamentDetailState( + {final TournamentModel? tournament, + final bool loading, + final Object? error}) = _$TournamentDetailStateImpl; + + @override + TournamentModel? get tournament; + @override + bool get loading; + @override + Object? get error; + + /// Create a copy of TournamentDetailState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TournamentDetailStateImplCopyWith<_$TournamentDetailStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index 7c40e06a..85c43c86 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -7,6 +7,7 @@ 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/formatter/date_formatter.dart'; +import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/tournament/tournament_list_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/extensions/context_extensions.dart'; @@ -122,7 +123,8 @@ class _TournamentListScreenState extends ConsumerState Widget _tournamentItem(BuildContext context, TournamentModel tournament) { return OnTapScale( - onTap: () {}, + onTap: () => + AppRoute.tournamentDetail(tournamentId: tournament.id).push(context), child: Container( padding: const EdgeInsets.all(16), margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), From 5c90edb49d7d1368b3ca6ace153c8440a20ae512 Mon Sep 17 00:00:00 2001 From: sidhdhi canopas <122426509+cp-sidhdhi-p@users.noreply.github.com> Date: Fri, 11 Oct 2024 15:53:22 +0530 Subject: [PATCH 16/33] Add match setting in sub collection (#116) * add match setting in sub collection * minor changes * rename identifier * fis dispose console error --- .../ball_score/ball_score_model.freezed.dart | 238 ++++++++++-- .../lib/api/innings/inning_model.freezed.dart | 21 +- data/lib/api/match/match_model.dart | 21 +- data/lib/api/match/match_model.freezed.dart | 355 ++++++++++++++++-- data/lib/api/match/match_model.g.dart | 17 + .../api/support/support_models.freezed.dart | 38 +- data/lib/api/team/team_model.freezed.dart | 44 ++- data/lib/api/user/user_models.freezed.dart | 42 ++- data/lib/service/match/match_service.dart | 24 ++ .../utils/constant/firestore_constant.dart | 2 + khelo/assets/locales/app_en.arb | 4 +- .../select_fielding_position_sheet.dart | 4 +- .../components/select_player_sheet.dart | 3 +- .../flow/score_board/score_board_screen.dart | 59 +-- .../score_board/score_board_view_model.dart | 95 ++++- .../score_board_view_model.freezed.dart | 103 ++--- 16 files changed, 867 insertions(+), 203 deletions(-) diff --git a/data/lib/api/ball_score/ball_score_model.freezed.dart b/data/lib/api/ball_score/ball_score_model.freezed.dart index db2916ef..fc687cb4 100644 --- a/data/lib/api/ball_score/ball_score_model.freezed.dart +++ b/data/lib/api/ball_score/ball_score_model.freezed.dart @@ -42,8 +42,12 @@ mixin _$BallScoreModel { @TimeStampJsonConverter() DateTime? get score_time => throw _privateConstructorUsedError; + /// Serializes this BallScoreModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of BallScoreModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $BallScoreModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -86,6 +90,8 @@ class _$BallScoreModelCopyWithImpl<$Res, $Val extends BallScoreModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of BallScoreModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -228,6 +234,8 @@ class __$$BallScoreModelImplCopyWithImpl<$Res> _$BallScoreModelImpl _value, $Res Function(_$BallScoreModelImpl) _then) : super(_value, _then); + /// Create a copy of BallScoreModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -445,7 +453,7 @@ class _$BallScoreModelImpl implements _BallScoreModel { other.score_time == score_time)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hashAll([ runtimeType, @@ -470,7 +478,9 @@ class _$BallScoreModelImpl implements _BallScoreModel { score_time ]); - @JsonKey(ignore: true) + /// Create a copy of BallScoreModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$BallScoreModelImplCopyWith<_$BallScoreModelImpl> get copyWith => @@ -550,8 +560,11 @@ abstract class _BallScoreModel implements BallScoreModel { @override @TimeStampJsonConverter() DateTime? get score_time; + + /// Create a copy of BallScoreModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$BallScoreModelImplCopyWith<_$BallScoreModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -562,7 +575,9 @@ mixin _$UserStat { BowlingStat? get bowlingStat => throw _privateConstructorUsedError; FieldingStat? get fieldingStat => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $UserStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -592,6 +607,8 @@ class _$UserStatCopyWithImpl<$Res, $Val extends UserStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -615,6 +632,8 @@ class _$UserStatCopyWithImpl<$Res, $Val extends UserStat> ) as $Val); } + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $BattingStatCopyWith<$Res>? get battingStat { @@ -627,6 +646,8 @@ class _$UserStatCopyWithImpl<$Res, $Val extends UserStat> }); } + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $BowlingStatCopyWith<$Res>? get bowlingStat { @@ -639,6 +660,8 @@ class _$UserStatCopyWithImpl<$Res, $Val extends UserStat> }); } + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $FieldingStatCopyWith<$Res>? get fieldingStat { @@ -681,6 +704,8 @@ class __$$UserStatImplCopyWithImpl<$Res> _$UserStatImpl _value, $Res Function(_$UserStatImpl) _then) : super(_value, _then); + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -739,7 +764,9 @@ class _$UserStatImpl implements _UserStat { int get hashCode => Object.hash(runtimeType, battingStat, bowlingStat, fieldingStat); - @JsonKey(ignore: true) + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$UserStatImplCopyWith<_$UserStatImpl> get copyWith => @@ -758,8 +785,11 @@ abstract class _UserStat implements UserStat { BowlingStat? get bowlingStat; @override FieldingStat? get fieldingStat; + + /// Create a copy of UserStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$UserStatImplCopyWith<_$UserStatImpl> get copyWith => throw _privateConstructorUsedError; } @@ -777,7 +807,9 @@ mixin _$BattingStat { int get hundreds => throw _privateConstructorUsedError; int get ducks => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of BattingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $BattingStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -811,6 +843,8 @@ class _$BattingStatCopyWithImpl<$Res, $Val extends BattingStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of BattingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -899,6 +933,8 @@ class __$$BattingStatImplCopyWithImpl<$Res> _$BattingStatImpl _value, $Res Function(_$BattingStatImpl) _then) : super(_value, _then); + /// Create a copy of BattingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1034,7 +1070,9 @@ class _$BattingStatImpl implements _BattingStat { int get hashCode => Object.hash(runtimeType, innings, runScored, average, strikeRate, ballFaced, fours, sixes, fifties, hundreds, ducks); - @JsonKey(ignore: true) + /// Create a copy of BattingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$BattingStatImplCopyWith<_$BattingStatImpl> get copyWith => @@ -1074,8 +1112,11 @@ abstract class _BattingStat implements BattingStat { int get hundreds; @override int get ducks; + + /// Create a copy of BattingStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$BattingStatImplCopyWith<_$BattingStatImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1093,7 +1134,9 @@ mixin _$BowlingStat { double get strikeRate => throw _privateConstructorUsedError; double get economyRate => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of BowlingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $BowlingStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1127,6 +1170,8 @@ class _$BowlingStatCopyWithImpl<$Res, $Val extends BowlingStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of BowlingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1215,6 +1260,8 @@ class __$$BowlingStatImplCopyWithImpl<$Res> _$BowlingStatImpl _value, $Res Function(_$BowlingStatImpl) _then) : super(_value, _then); + /// Create a copy of BowlingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1361,7 +1408,9 @@ class _$BowlingStatImpl implements _BowlingStat { strikeRate, economyRate); - @JsonKey(ignore: true) + /// Create a copy of BowlingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$BowlingStatImplCopyWith<_$BowlingStatImpl> get copyWith => @@ -1401,8 +1450,11 @@ abstract class _BowlingStat implements BowlingStat { double get strikeRate; @override double get economyRate; + + /// Create a copy of BowlingStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$BowlingStatImplCopyWith<_$BowlingStatImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1413,7 +1465,9 @@ mixin _$FieldingStat { int get runOut => throw _privateConstructorUsedError; int get stumping => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of FieldingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $FieldingStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1437,6 +1491,8 @@ class _$FieldingStatCopyWithImpl<$Res, $Val extends FieldingStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of FieldingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1480,6 +1536,8 @@ class __$$FieldingStatImplCopyWithImpl<$Res> _$FieldingStatImpl _value, $Res Function(_$FieldingStatImpl) _then) : super(_value, _then); + /// Create a copy of FieldingStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1539,7 +1597,9 @@ class _$FieldingStatImpl implements _FieldingStat { @override int get hashCode => Object.hash(runtimeType, catches, runOut, stumping); - @JsonKey(ignore: true) + /// Create a copy of FieldingStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$FieldingStatImplCopyWith<_$FieldingStatImpl> get copyWith => @@ -1558,8 +1618,11 @@ abstract class _FieldingStat implements FieldingStat { int get runOut; @override int get stumping; + + /// Create a copy of FieldingStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$FieldingStatImplCopyWith<_$FieldingStatImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1574,8 +1637,12 @@ mixin _$OverStatModel { int get wicket => throw _privateConstructorUsedError; int get extra => throw _privateConstructorUsedError; + /// Serializes this OverStatModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of OverStatModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $OverStatModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1599,6 +1666,8 @@ class _$OverStatModelCopyWithImpl<$Res, $Val extends OverStatModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of OverStatModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1642,6 +1711,8 @@ class __$$OverStatModelImplCopyWithImpl<$Res> _$OverStatModelImpl _value, $Res Function(_$OverStatModelImpl) _then) : super(_value, _then); + /// Create a copy of OverStatModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1699,11 +1770,13 @@ class _$OverStatModelImpl implements _OverStatModel { (identical(other.extra, extra) || other.extra == extra)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, run, wicket, extra); - @JsonKey(ignore: true) + /// Create a copy of OverStatModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$OverStatModelImplCopyWith<_$OverStatModelImpl> get copyWith => @@ -1730,8 +1803,11 @@ abstract class _OverStatModel implements OverStatModel { int get wicket; @override int get extra; + + /// Create a copy of OverStatModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$OverStatModelImplCopyWith<_$OverStatModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1747,8 +1823,12 @@ mixin _$TeamRunStat { int get wicket => throw _privateConstructorUsedError; double get over => throw _privateConstructorUsedError; + /// Serializes this TeamRunStat to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of TeamRunStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TeamRunStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1772,6 +1852,8 @@ class _$TeamRunStatCopyWithImpl<$Res, $Val extends TeamRunStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TeamRunStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1820,6 +1902,8 @@ class __$$TeamRunStatImplCopyWithImpl<$Res> _$TeamRunStatImpl _value, $Res Function(_$TeamRunStatImpl) _then) : super(_value, _then); + /// Create a copy of TeamRunStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1888,11 +1972,13 @@ class _$TeamRunStatImpl implements _TeamRunStat { (identical(other.over, over) || other.over == over)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, teamName, run, wicket, over); - @JsonKey(ignore: true) + /// Create a copy of TeamRunStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TeamRunStatImplCopyWith<_$TeamRunStatImpl> get copyWith => @@ -1924,8 +2010,11 @@ abstract class _TeamRunStat implements TeamRunStat { int get wicket; @override double get over; + + /// Create a copy of TeamRunStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TeamRunStatImplCopyWith<_$TeamRunStatImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1944,7 +2033,9 @@ mixin _$OverSummary { int get totalRuns => throw _privateConstructorUsedError; int get totalWickets => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $OverSummaryCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1984,6 +2075,8 @@ class _$OverSummaryCopyWithImpl<$Res, $Val extends OverSummary> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2047,6 +2140,8 @@ class _$OverSummaryCopyWithImpl<$Res, $Val extends OverSummary> ) as $Val); } + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $BowlerSummaryCopyWith<$Res> get bowler { @@ -2055,6 +2150,8 @@ class _$OverSummaryCopyWithImpl<$Res, $Val extends OverSummary> }); } + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $BatsmanSummaryCopyWith<$Res> get striker { @@ -2063,6 +2160,8 @@ class _$OverSummaryCopyWithImpl<$Res, $Val extends OverSummary> }); } + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $BatsmanSummaryCopyWith<$Res> get nonStriker { @@ -2071,6 +2170,8 @@ class _$OverSummaryCopyWithImpl<$Res, $Val extends OverSummary> }); } + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $ExtraSummaryCopyWith<$Res> get extrasSummary { @@ -2119,6 +2220,8 @@ class __$$OverSummaryImplCopyWithImpl<$Res> _$OverSummaryImpl _value, $Res Function(_$OverSummaryImpl) _then) : super(_value, _then); + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2292,7 +2395,9 @@ class _$OverSummaryImpl implements _OverSummary { totalRuns, totalWickets); - @JsonKey(ignore: true) + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$OverSummaryImplCopyWith<_$OverSummaryImpl> get copyWith => @@ -2335,8 +2440,11 @@ abstract class _OverSummary implements OverSummary { int get totalRuns; @override int get totalWickets; + + /// Create a copy of OverSummary + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$OverSummaryImplCopyWith<_$OverSummaryImpl> get copyWith => throw _privateConstructorUsedError; } @@ -2353,7 +2461,9 @@ mixin _$BatsmanSummary { int get sixes => throw _privateConstructorUsedError; int get fours => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $BatsmanSummaryCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -2390,6 +2500,8 @@ class _$BatsmanSummaryCopyWithImpl<$Res, $Val extends BatsmanSummary> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2443,6 +2555,8 @@ class _$BatsmanSummaryCopyWithImpl<$Res, $Val extends BatsmanSummary> ) as $Val); } + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $UserModelCopyWith<$Res> get player { @@ -2451,6 +2565,8 @@ class _$BatsmanSummaryCopyWithImpl<$Res, $Val extends BatsmanSummary> }); } + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $PlayerCopyWith<$Res>? get ballBy { @@ -2463,6 +2579,8 @@ class _$BatsmanSummaryCopyWithImpl<$Res, $Val extends BatsmanSummary> }); } + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $PlayerCopyWith<$Res>? get catchBy { @@ -2511,6 +2629,8 @@ class __$$BatsmanSummaryImplCopyWithImpl<$Res> _$BatsmanSummaryImpl _value, $Res Function(_$BatsmanSummaryImpl) _then) : super(_value, _then); + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2631,7 +2751,9 @@ class _$BatsmanSummaryImpl implements _BatsmanSummary { int get hashCode => Object.hash(runtimeType, player, ballBy, catchBy, wicketType, outAtOver, runs, ballFaced, sixes, fours); - @JsonKey(ignore: true) + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$BatsmanSummaryImplCopyWith<_$BatsmanSummaryImpl> get copyWith => @@ -2669,8 +2791,11 @@ abstract class _BatsmanSummary implements BatsmanSummary { int get sixes; @override int get fours; + + /// Create a copy of BatsmanSummary + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$BatsmanSummaryImplCopyWith<_$BatsmanSummaryImpl> get copyWith => throw _privateConstructorUsedError; } @@ -2685,7 +2810,9 @@ mixin _$BowlerSummary { int get noBalls => throw _privateConstructorUsedError; int get wideBalls => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $BowlerSummaryCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -2718,6 +2845,8 @@ class _$BowlerSummaryCopyWithImpl<$Res, $Val extends BowlerSummary> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2761,6 +2890,8 @@ class _$BowlerSummaryCopyWithImpl<$Res, $Val extends BowlerSummary> ) as $Val); } + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $UserModelCopyWith<$Res> get player { @@ -2799,6 +2930,8 @@ class __$$BowlerSummaryImplCopyWithImpl<$Res> _$BowlerSummaryImpl _value, $Res Function(_$BowlerSummaryImpl) _then) : super(_value, _then); + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2903,7 +3036,9 @@ class _$BowlerSummaryImpl implements _BowlerSummary { int get hashCode => Object.hash(runtimeType, player, runsConceded, maiden, overDelivered, wicket, noBalls, wideBalls); - @JsonKey(ignore: true) + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$BowlerSummaryImplCopyWith<_$BowlerSummaryImpl> get copyWith => @@ -2934,8 +3069,11 @@ abstract class _BowlerSummary implements BowlerSummary { int get noBalls; @override int get wideBalls; + + /// Create a copy of BowlerSummary + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$BowlerSummaryImplCopyWith<_$BowlerSummaryImpl> get copyWith => throw _privateConstructorUsedError; } @@ -2945,7 +3083,9 @@ mixin _$Player { String get id => throw _privateConstructorUsedError; String get name => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of Player + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $PlayerCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -2967,6 +3107,8 @@ class _$PlayerCopyWithImpl<$Res, $Val extends Player> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of Player + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -3004,6 +3146,8 @@ class __$$PlayerImplCopyWithImpl<$Res> _$PlayerImpl _value, $Res Function(_$PlayerImpl) _then) : super(_value, _then); + /// Create a copy of Player + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -3052,7 +3196,9 @@ class _$PlayerImpl implements _Player { @override int get hashCode => Object.hash(runtimeType, id, name); - @JsonKey(ignore: true) + /// Create a copy of Player + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$PlayerImplCopyWith<_$PlayerImpl> get copyWith => @@ -3066,8 +3212,11 @@ abstract class _Player implements Player { String get id; @override String get name; + + /// Create a copy of Player + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$PlayerImplCopyWith<_$PlayerImpl> get copyWith => throw _privateConstructorUsedError; } @@ -3080,7 +3229,9 @@ mixin _$ExtraSummary { int get wideBall => throw _privateConstructorUsedError; int get penalty => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of ExtraSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ExtraSummaryCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -3104,6 +3255,8 @@ class _$ExtraSummaryCopyWithImpl<$Res, $Val extends ExtraSummary> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ExtraSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -3157,6 +3310,8 @@ class __$$ExtraSummaryImplCopyWithImpl<$Res> _$ExtraSummaryImpl _value, $Res Function(_$ExtraSummaryImpl) _then) : super(_value, _then); + /// Create a copy of ExtraSummary + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -3239,7 +3394,9 @@ class _$ExtraSummaryImpl implements _ExtraSummary { int get hashCode => Object.hash(runtimeType, bye, legBye, noBall, wideBall, penalty); - @JsonKey(ignore: true) + /// Create a copy of ExtraSummary + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ExtraSummaryImplCopyWith<_$ExtraSummaryImpl> get copyWith => @@ -3264,8 +3421,11 @@ abstract class _ExtraSummary implements ExtraSummary { int get wideBall; @override int get penalty; + + /// Create a copy of ExtraSummary + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ExtraSummaryImplCopyWith<_$ExtraSummaryImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/innings/inning_model.freezed.dart b/data/lib/api/innings/inning_model.freezed.dart index b5f3d90e..bd216a14 100644 --- a/data/lib/api/innings/inning_model.freezed.dart +++ b/data/lib/api/innings/inning_model.freezed.dart @@ -29,8 +29,12 @@ mixin _$InningModel { int get total_wickets => throw _privateConstructorUsedError; InningStatus? get innings_status => throw _privateConstructorUsedError; + /// Serializes this InningModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of InningModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $InningModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -62,6 +66,8 @@ class _$InningModelCopyWithImpl<$Res, $Val extends InningModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of InningModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -138,6 +144,8 @@ class __$$InningModelImplCopyWithImpl<$Res> _$InningModelImpl _value, $Res Function(_$InningModelImpl) _then) : super(_value, _then); + /// Create a copy of InningModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -248,12 +256,14 @@ class _$InningModelImpl implements _InningModel { other.innings_status == innings_status)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, id, match_id, team_id, overs, index, total_runs, total_wickets, innings_status); - @JsonKey(ignore: true) + /// Create a copy of InningModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$InningModelImplCopyWith<_$InningModelImpl> get copyWith => @@ -297,8 +307,11 @@ abstract class _InningModel implements InningModel { int get total_wickets; @override InningStatus? get innings_status; + + /// Create a copy of InningModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$InningModelImplCopyWith<_$InningModelImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/match/match_model.dart b/data/lib/api/match/match_model.dart index 21988b9c..66e2dd25 100644 --- a/data/lib/api/match/match_model.dart +++ b/data/lib/api/match/match_model.dart @@ -1,11 +1,12 @@ // ignore_for_file: non_constant_identifier_names import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + import '../../converter/timestamp_json_converter.dart'; import '../../extensions/double_extensions.dart'; import '../team/team_model.dart'; import '../user/user_models.dart'; -import 'package:freezed_annotation/freezed_annotation.dart'; part 'match_model.freezed.dart'; @@ -61,6 +62,24 @@ class MatchModel with _$MatchModel { MatchModel.fromJson(snapshot.data()!); } +@freezed +class MatchSetting with _$MatchSetting { + const factory MatchSetting({ + @Default(true) bool continue_with_injured_player, + @Default(true) bool show_wagon_wheel_for_less_run, + @Default(true) bool show_wagon_wheel_for_dot_ball, + }) = _MatchSetting; + + factory MatchSetting.fromJson(Map json) => + _$MatchSettingFromJson(json); + + factory MatchSetting.fromFireStore( + DocumentSnapshot> snapshot, + SnapshotOptions? options, + ) => + MatchSetting.fromJson(snapshot.data()!); +} + @freezed class MatchTeamModel with _$MatchTeamModel { @JsonSerializable(anyMap: true, explicitToJson: true) diff --git a/data/lib/api/match/match_model.freezed.dart b/data/lib/api/match/match_model.freezed.dart index 8f27cbf1..2050de3e 100644 --- a/data/lib/api/match/match_model.freezed.dart +++ b/data/lib/api/match/match_model.freezed.dart @@ -57,8 +57,12 @@ mixin _$MatchModel { String? get current_playing_team_id => throw _privateConstructorUsedError; RevisedTarget? get revised_target => throw _privateConstructorUsedError; + /// Serializes this MatchModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $MatchModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -119,6 +123,8 @@ class _$MatchModelCopyWithImpl<$Res, $Val extends MatchModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -282,6 +288,8 @@ class _$MatchModelCopyWithImpl<$Res, $Val extends MatchModel> ) as $Val); } + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $UserModelCopyWith<$Res>? get referee { @@ -294,6 +302,8 @@ class _$MatchModelCopyWithImpl<$Res, $Val extends MatchModel> }); } + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $RevisedTargetCopyWith<$Res>? get revised_target { @@ -365,6 +375,8 @@ class __$$MatchModelImplCopyWithImpl<$Res> _$MatchModelImpl _value, $Res Function(_$MatchModelImpl) _then) : super(_value, _then); + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -819,7 +831,7 @@ class _$MatchModelImpl implements _MatchModel { other.revised_target == revised_target)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hashAll([ runtimeType, @@ -856,7 +868,9 @@ class _$MatchModelImpl implements _MatchModel { revised_target ]); - @JsonKey(ignore: true) + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$MatchModelImplCopyWith<_$MatchModelImpl> get copyWith => @@ -978,12 +992,221 @@ abstract class _MatchModel implements MatchModel { String? get current_playing_team_id; @override RevisedTarget? get revised_target; + + /// Create a copy of MatchModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$MatchModelImplCopyWith<_$MatchModelImpl> get copyWith => throw _privateConstructorUsedError; } +MatchSetting _$MatchSettingFromJson(Map json) { + return _MatchSetting.fromJson(json); +} + +/// @nodoc +mixin _$MatchSetting { + bool get continue_with_injured_player => throw _privateConstructorUsedError; + bool get show_wagon_wheel_for_less_run => throw _privateConstructorUsedError; + bool get show_wagon_wheel_for_dot_ball => throw _privateConstructorUsedError; + + /// Serializes this MatchSetting to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + + /// Create a copy of MatchSetting + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $MatchSettingCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $MatchSettingCopyWith<$Res> { + factory $MatchSettingCopyWith( + MatchSetting value, $Res Function(MatchSetting) then) = + _$MatchSettingCopyWithImpl<$Res, MatchSetting>; + @useResult + $Res call( + {bool continue_with_injured_player, + bool show_wagon_wheel_for_less_run, + bool show_wagon_wheel_for_dot_ball}); +} + +/// @nodoc +class _$MatchSettingCopyWithImpl<$Res, $Val extends MatchSetting> + implements $MatchSettingCopyWith<$Res> { + _$MatchSettingCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of MatchSetting + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? continue_with_injured_player = null, + Object? show_wagon_wheel_for_less_run = null, + Object? show_wagon_wheel_for_dot_ball = null, + }) { + return _then(_value.copyWith( + continue_with_injured_player: null == continue_with_injured_player + ? _value.continue_with_injured_player + : continue_with_injured_player // ignore: cast_nullable_to_non_nullable + as bool, + show_wagon_wheel_for_less_run: null == show_wagon_wheel_for_less_run + ? _value.show_wagon_wheel_for_less_run + : show_wagon_wheel_for_less_run // ignore: cast_nullable_to_non_nullable + as bool, + show_wagon_wheel_for_dot_ball: null == show_wagon_wheel_for_dot_ball + ? _value.show_wagon_wheel_for_dot_ball + : show_wagon_wheel_for_dot_ball // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$MatchSettingImplCopyWith<$Res> + implements $MatchSettingCopyWith<$Res> { + factory _$$MatchSettingImplCopyWith( + _$MatchSettingImpl value, $Res Function(_$MatchSettingImpl) then) = + __$$MatchSettingImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {bool continue_with_injured_player, + bool show_wagon_wheel_for_less_run, + bool show_wagon_wheel_for_dot_ball}); +} + +/// @nodoc +class __$$MatchSettingImplCopyWithImpl<$Res> + extends _$MatchSettingCopyWithImpl<$Res, _$MatchSettingImpl> + implements _$$MatchSettingImplCopyWith<$Res> { + __$$MatchSettingImplCopyWithImpl( + _$MatchSettingImpl _value, $Res Function(_$MatchSettingImpl) _then) + : super(_value, _then); + + /// Create a copy of MatchSetting + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? continue_with_injured_player = null, + Object? show_wagon_wheel_for_less_run = null, + Object? show_wagon_wheel_for_dot_ball = null, + }) { + return _then(_$MatchSettingImpl( + continue_with_injured_player: null == continue_with_injured_player + ? _value.continue_with_injured_player + : continue_with_injured_player // ignore: cast_nullable_to_non_nullable + as bool, + show_wagon_wheel_for_less_run: null == show_wagon_wheel_for_less_run + ? _value.show_wagon_wheel_for_less_run + : show_wagon_wheel_for_less_run // ignore: cast_nullable_to_non_nullable + as bool, + show_wagon_wheel_for_dot_ball: null == show_wagon_wheel_for_dot_ball + ? _value.show_wagon_wheel_for_dot_ball + : show_wagon_wheel_for_dot_ball // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc +@JsonSerializable() +class _$MatchSettingImpl implements _MatchSetting { + const _$MatchSettingImpl( + {this.continue_with_injured_player = true, + this.show_wagon_wheel_for_less_run = true, + this.show_wagon_wheel_for_dot_ball = true}); + + factory _$MatchSettingImpl.fromJson(Map json) => + _$$MatchSettingImplFromJson(json); + + @override + @JsonKey() + final bool continue_with_injured_player; + @override + @JsonKey() + final bool show_wagon_wheel_for_less_run; + @override + @JsonKey() + final bool show_wagon_wheel_for_dot_ball; + + @override + String toString() { + return 'MatchSetting(continue_with_injured_player: $continue_with_injured_player, show_wagon_wheel_for_less_run: $show_wagon_wheel_for_less_run, show_wagon_wheel_for_dot_ball: $show_wagon_wheel_for_dot_ball)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$MatchSettingImpl && + (identical(other.continue_with_injured_player, + continue_with_injured_player) || + other.continue_with_injured_player == + continue_with_injured_player) && + (identical(other.show_wagon_wheel_for_less_run, + show_wagon_wheel_for_less_run) || + other.show_wagon_wheel_for_less_run == + show_wagon_wheel_for_less_run) && + (identical(other.show_wagon_wheel_for_dot_ball, + show_wagon_wheel_for_dot_ball) || + other.show_wagon_wheel_for_dot_ball == + show_wagon_wheel_for_dot_ball)); + } + + @JsonKey(includeFromJson: false, includeToJson: false) + @override + int get hashCode => Object.hash(runtimeType, continue_with_injured_player, + show_wagon_wheel_for_less_run, show_wagon_wheel_for_dot_ball); + + /// Create a copy of MatchSetting + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$MatchSettingImplCopyWith<_$MatchSettingImpl> get copyWith => + __$$MatchSettingImplCopyWithImpl<_$MatchSettingImpl>(this, _$identity); + + @override + Map toJson() { + return _$$MatchSettingImplToJson( + this, + ); + } +} + +abstract class _MatchSetting implements MatchSetting { + const factory _MatchSetting( + {final bool continue_with_injured_player, + final bool show_wagon_wheel_for_less_run, + final bool show_wagon_wheel_for_dot_ball}) = _$MatchSettingImpl; + + factory _MatchSetting.fromJson(Map json) = + _$MatchSettingImpl.fromJson; + + @override + bool get continue_with_injured_player; + @override + bool get show_wagon_wheel_for_less_run; + @override + bool get show_wagon_wheel_for_dot_ball; + + /// Create a copy of MatchSetting + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$MatchSettingImplCopyWith<_$MatchSettingImpl> get copyWith => + throw _privateConstructorUsedError; +} + MatchTeamModel _$MatchTeamModelFromJson(Map json) { return _MatchTeamModel.fromJson(json); } @@ -1000,8 +1223,12 @@ mixin _$MatchTeamModel { int get wicket => throw _privateConstructorUsedError; List get squad => throw _privateConstructorUsedError; + /// Serializes this MatchTeamModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $MatchTeamModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1035,6 +1262,8 @@ class _$MatchTeamModelCopyWithImpl<$Res, $Val extends MatchTeamModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1083,6 +1312,8 @@ class _$MatchTeamModelCopyWithImpl<$Res, $Val extends MatchTeamModel> ) as $Val); } + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $TeamModelCopyWith<$Res> get team { @@ -1122,6 +1353,8 @@ class __$$MatchTeamModelImplCopyWithImpl<$Res> _$MatchTeamModelImpl _value, $Res Function(_$MatchTeamModelImpl) _then) : super(_value, _then); + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1239,12 +1472,14 @@ class _$MatchTeamModelImpl implements _MatchTeamModel { const DeepCollectionEquality().equals(other._squad, _squad)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, team, team_id, captain_id, admin_id, over, run, wicket, const DeepCollectionEquality().hash(_squad)); - @JsonKey(ignore: true) + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$MatchTeamModelImplCopyWith<_$MatchTeamModelImpl> get copyWith => @@ -1291,8 +1526,11 @@ abstract class _MatchTeamModel implements MatchTeamModel { int get wicket; @override List get squad; + + /// Create a copy of MatchTeamModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$MatchTeamModelImplCopyWith<_$MatchTeamModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1309,8 +1547,12 @@ mixin _$MatchPlayer { List get performance => throw _privateConstructorUsedError; PlayerStatus get status => throw _privateConstructorUsedError; + /// Serializes this MatchPlayer to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $MatchPlayerCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1340,6 +1582,8 @@ class _$MatchPlayerCopyWithImpl<$Res, $Val extends MatchPlayer> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1368,6 +1612,8 @@ class _$MatchPlayerCopyWithImpl<$Res, $Val extends MatchPlayer> ) as $Val); } + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $UserModelCopyWith<$Res> get player { @@ -1403,6 +1649,8 @@ class __$$MatchPlayerImplCopyWithImpl<$Res> _$MatchPlayerImpl _value, $Res Function(_$MatchPlayerImpl) _then) : super(_value, _then); + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1482,12 +1730,14 @@ class _$MatchPlayerImpl implements _MatchPlayer { (identical(other.status, status) || other.status == status)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, player, id, const DeepCollectionEquality().hash(_performance), status); - @JsonKey(ignore: true) + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$MatchPlayerImplCopyWith<_$MatchPlayerImpl> get copyWith => @@ -1521,8 +1771,11 @@ abstract class _MatchPlayer implements MatchPlayer { List get performance; @override PlayerStatus get status; + + /// Create a copy of MatchPlayer + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$MatchPlayerImplCopyWith<_$MatchPlayerImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1537,8 +1790,12 @@ mixin _$PlayerPerformance { PlayerStatus? get status => throw _privateConstructorUsedError; int? get index => throw _privateConstructorUsedError; + /// Serializes this PlayerPerformance to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of PlayerPerformance + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $PlayerPerformanceCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1562,6 +1819,8 @@ class _$PlayerPerformanceCopyWithImpl<$Res, $Val extends PlayerPerformance> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of PlayerPerformance + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1605,6 +1864,8 @@ class __$$PlayerPerformanceImplCopyWithImpl<$Res> $Res Function(_$PlayerPerformanceImpl) _then) : super(_value, _then); + /// Create a copy of PlayerPerformance + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1661,11 +1922,13 @@ class _$PlayerPerformanceImpl implements _PlayerPerformance { (identical(other.index, index) || other.index == index)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, inning_id, status, index); - @JsonKey(ignore: true) + /// Create a copy of PlayerPerformance + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$PlayerPerformanceImplCopyWith<_$PlayerPerformanceImpl> get copyWith => @@ -1695,8 +1958,11 @@ abstract class _PlayerPerformance implements PlayerPerformance { PlayerStatus? get status; @override int? get index; + + /// Create a copy of PlayerPerformance + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$PlayerPerformanceImplCopyWith<_$PlayerPerformanceImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1713,8 +1979,12 @@ mixin _$RevisedTarget { @TimeStampJsonConverter() DateTime? get revised_time => throw _privateConstructorUsedError; + /// Serializes this RevisedTarget to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of RevisedTarget + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $RevisedTargetCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1742,6 +2012,8 @@ class _$RevisedTargetCopyWithImpl<$Res, $Val extends RevisedTarget> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of RevisedTarget + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1794,6 +2066,8 @@ class __$$RevisedTargetImplCopyWithImpl<$Res> _$RevisedTargetImpl _value, $Res Function(_$RevisedTargetImpl) _then) : super(_value, _then); + /// Create a copy of RevisedTarget + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1864,11 +2138,13 @@ class _$RevisedTargetImpl implements _RevisedTarget { other.revised_time == revised_time)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, runs, overs, time, revised_time); - @JsonKey(ignore: true) + /// Create a copy of RevisedTarget + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$RevisedTargetImplCopyWith<_$RevisedTargetImpl> get copyWith => @@ -1902,8 +2178,11 @@ abstract class _RevisedTarget implements RevisedTarget { @override @TimeStampJsonConverter() DateTime? get revised_time; + + /// Create a copy of RevisedTarget + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$RevisedTargetImplCopyWith<_$RevisedTargetImpl> get copyWith => throw _privateConstructorUsedError; } @@ -1914,7 +2193,9 @@ mixin _$TeamMatchStatus { int get tie => throw _privateConstructorUsedError; int get lost => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of TeamMatchStatus + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TeamMatchStatusCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -1938,6 +2219,8 @@ class _$TeamMatchStatusCopyWithImpl<$Res, $Val extends TeamMatchStatus> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TeamMatchStatus + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -1981,6 +2264,8 @@ class __$$TeamMatchStatusImplCopyWithImpl<$Res> _$TeamMatchStatusImpl _value, $Res Function(_$TeamMatchStatusImpl) _then) : super(_value, _then); + /// Create a copy of TeamMatchStatus + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2038,7 +2323,9 @@ class _$TeamMatchStatusImpl implements _TeamMatchStatus { @override int get hashCode => Object.hash(runtimeType, win, tie, lost); - @JsonKey(ignore: true) + /// Create a copy of TeamMatchStatus + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TeamMatchStatusImplCopyWith<_$TeamMatchStatusImpl> get copyWith => @@ -2056,8 +2343,11 @@ abstract class _TeamMatchStatus implements TeamMatchStatus { int get tie; @override int get lost; + + /// Create a copy of TeamMatchStatus + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TeamMatchStatusImplCopyWith<_$TeamMatchStatusImpl> get copyWith => throw _privateConstructorUsedError; } @@ -2074,7 +2364,9 @@ mixin _$TeamStat { int get lowest_runs => throw _privateConstructorUsedError; double get run_rate => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TeamStatCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -2108,6 +2400,8 @@ class _$TeamStatCopyWithImpl<$Res, $Val extends TeamStat> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2161,6 +2455,8 @@ class _$TeamStatCopyWithImpl<$Res, $Val extends TeamStat> ) as $Val); } + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $TeamMatchStatusCopyWith<$Res> get status { @@ -2201,6 +2497,8 @@ class __$$TeamStatImplCopyWithImpl<$Res> _$TeamStatImpl _value, $Res Function(_$TeamStatImpl) _then) : super(_value, _then); + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -2327,7 +2625,9 @@ class _$TeamStatImpl implements _TeamStat { int get hashCode => Object.hash(runtimeType, played, status, runs, wickets, batting_average, bowling_average, highest_runs, lowest_runs, run_rate); - @JsonKey(ignore: true) + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TeamStatImplCopyWith<_$TeamStatImpl> get copyWith => @@ -2364,8 +2664,11 @@ abstract class _TeamStat implements TeamStat { int get lowest_runs; @override double get run_rate; + + /// Create a copy of TeamStat + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TeamStatImplCopyWith<_$TeamStatImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/match/match_model.g.dart b/data/lib/api/match/match_model.g.dart index 5127991e..0e8731de 100644 --- a/data/lib/api/match/match_model.g.dart +++ b/data/lib/api/match/match_model.g.dart @@ -148,6 +148,23 @@ Json? _$JsonConverterToJson( ) => value == null ? null : toJson(value); +_$MatchSettingImpl _$$MatchSettingImplFromJson(Map json) => + _$MatchSettingImpl( + continue_with_injured_player: + json['continue_with_injured_player'] as bool? ?? true, + show_wagon_wheel_for_less_run: + json['show_wagon_wheel_for_less_run'] as bool? ?? true, + show_wagon_wheel_for_dot_ball: + json['show_wagon_wheel_for_dot_ball'] as bool? ?? true, + ); + +Map _$$MatchSettingImplToJson(_$MatchSettingImpl instance) => + { + 'continue_with_injured_player': instance.continue_with_injured_player, + 'show_wagon_wheel_for_less_run': instance.show_wagon_wheel_for_less_run, + 'show_wagon_wheel_for_dot_ball': instance.show_wagon_wheel_for_dot_ball, + }; + _$MatchTeamModelImpl _$$MatchTeamModelImplFromJson(Map json) => _$MatchTeamModelImpl( team_id: json['team_id'] as String, diff --git a/data/lib/api/support/support_models.freezed.dart b/data/lib/api/support/support_models.freezed.dart index 3ea16c58..d7c85808 100644 --- a/data/lib/api/support/support_models.freezed.dart +++ b/data/lib/api/support/support_models.freezed.dart @@ -30,8 +30,12 @@ mixin _$AddSupportCaseRequest { @TimeStampJsonConverter() DateTime? get createdTime => throw _privateConstructorUsedError; + /// Serializes this AddSupportCaseRequest to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of AddSupportCaseRequest + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $AddSupportCaseRequestCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -63,6 +67,8 @@ class _$AddSupportCaseRequestCopyWithImpl<$Res, // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of AddSupportCaseRequest + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -135,6 +141,8 @@ class __$$AddSupportCaseRequestImplCopyWithImpl<$Res> $Res Function(_$AddSupportCaseRequestImpl) _then) : super(_value, _then); + /// Create a copy of AddSupportCaseRequest + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -241,7 +249,7 @@ class _$AddSupportCaseRequestImpl implements _AddSupportCaseRequest { other.createdTime == createdTime)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -253,7 +261,9 @@ class _$AddSupportCaseRequestImpl implements _AddSupportCaseRequest { createdAt, createdTime); - @JsonKey(ignore: true) + /// Create a copy of AddSupportCaseRequest + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$AddSupportCaseRequestImplCopyWith<_$AddSupportCaseRequestImpl> @@ -297,8 +307,11 @@ abstract class _AddSupportCaseRequest implements AddSupportCaseRequest { @override @TimeStampJsonConverter() DateTime? get createdTime; + + /// Create a copy of AddSupportCaseRequest + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$AddSupportCaseRequestImplCopyWith<_$AddSupportCaseRequestImpl> get copyWith => throw _privateConstructorUsedError; } @@ -310,7 +323,9 @@ mixin _$Attachment { String get name => throw _privateConstructorUsedError; AttachmentUploadStatus get uploadStatus => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + /// Create a copy of Attachment + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $AttachmentCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -338,6 +353,8 @@ class _$AttachmentCopyWithImpl<$Res, $Val extends Attachment> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of Attachment + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -390,6 +407,8 @@ class __$$AttachmentImplCopyWithImpl<$Res> _$AttachmentImpl _value, $Res Function(_$AttachmentImpl) _then) : super(_value, _then); + /// Create a copy of Attachment + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -459,7 +478,9 @@ class _$AttachmentImpl implements _Attachment { @override int get hashCode => Object.hash(runtimeType, path, url, name, uploadStatus); - @JsonKey(ignore: true) + /// Create a copy of Attachment + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$AttachmentImplCopyWith<_$AttachmentImpl> get copyWith => @@ -481,8 +502,11 @@ abstract class _Attachment implements Attachment { String get name; @override AttachmentUploadStatus get uploadStatus; + + /// Create a copy of Attachment + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$AttachmentImplCopyWith<_$AttachmentImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/team/team_model.freezed.dart b/data/lib/api/team/team_model.freezed.dart index 0e3a8d45..18fc495c 100644 --- a/data/lib/api/team/team_model.freezed.dart +++ b/data/lib/api/team/team_model.freezed.dart @@ -33,8 +33,12 @@ mixin _$TeamModel { @JsonKey(name: FireStoreConst.teamPlayers) List get players => throw _privateConstructorUsedError; + /// Serializes this TeamModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of TeamModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TeamModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -67,6 +71,8 @@ class _$TeamModelCopyWithImpl<$Res, $Val extends TeamModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TeamModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -155,6 +161,8 @@ class __$$TeamModelImplCopyWithImpl<$Res> _$TeamModelImpl _value, $Res Function(_$TeamModelImpl) _then) : super(_value, _then); + /// Create a copy of TeamModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -291,7 +299,7 @@ class _$TeamModelImpl implements _TeamModel { const DeepCollectionEquality().equals(other._players, _players)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -306,7 +314,9 @@ class _$TeamModelImpl implements _TeamModel { created_time, const DeepCollectionEquality().hash(_players)); - @JsonKey(ignore: true) + /// Create a copy of TeamModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TeamModelImplCopyWith<_$TeamModelImpl> get copyWith => @@ -359,8 +369,11 @@ abstract class _TeamModel implements TeamModel { @override @JsonKey(name: FireStoreConst.teamPlayers) List get players; + + /// Create a copy of TeamModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TeamModelImplCopyWith<_$TeamModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -376,8 +389,12 @@ mixin _$TeamPlayer { @JsonKey(includeToJson: false, includeFromJson: false) UserModel get user => throw _privateConstructorUsedError; + /// Serializes this TeamPlayer to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $TeamPlayerCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -406,6 +423,8 @@ class _$TeamPlayerCopyWithImpl<$Res, $Val extends TeamPlayer> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -429,6 +448,8 @@ class _$TeamPlayerCopyWithImpl<$Res, $Val extends TeamPlayer> ) as $Val); } + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. @override @pragma('vm:prefer-inline') $UserModelCopyWith<$Res> get user { @@ -463,6 +484,8 @@ class __$$TeamPlayerImplCopyWithImpl<$Res> _$TeamPlayerImpl _value, $Res Function(_$TeamPlayerImpl) _then) : super(_value, _then); + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -524,11 +547,13 @@ class _$TeamPlayerImpl implements _TeamPlayer { (identical(other.user, user) || other.user == user)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, id, role, user); - @JsonKey(ignore: true) + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$TeamPlayerImplCopyWith<_$TeamPlayerImpl> get copyWith => @@ -559,8 +584,11 @@ abstract class _TeamPlayer implements TeamPlayer { @override @JsonKey(includeToJson: false, includeFromJson: false) UserModel get user; + + /// Create a copy of TeamPlayer + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$TeamPlayerImplCopyWith<_$TeamPlayerImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/api/user/user_models.freezed.dart b/data/lib/api/user/user_models.freezed.dart index b3dcf546..026870d9 100644 --- a/data/lib/api/user/user_models.freezed.dart +++ b/data/lib/api/user/user_models.freezed.dart @@ -37,8 +37,12 @@ mixin _$UserModel { bool get isActive => throw _privateConstructorUsedError; bool get notifications => throw _privateConstructorUsedError; + /// Serializes this UserModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of UserModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $UserModelCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -77,6 +81,8 @@ class _$UserModelCopyWithImpl<$Res, $Val extends UserModel> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of UserModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -201,6 +207,8 @@ class __$$UserModelImplCopyWithImpl<$Res> _$UserModelImpl _value, $Res Function(_$UserModelImpl) _then) : super(_value, _then); + /// Create a copy of UserModel + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -388,7 +396,7 @@ class _$UserModelImpl extends _UserModel { other.notifications == notifications)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -409,7 +417,9 @@ class _$UserModelImpl extends _UserModel { isActive, notifications); - @JsonKey(ignore: true) + /// Create a copy of UserModel + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$UserModelImplCopyWith<_$UserModelImpl> get copyWith => @@ -478,8 +488,11 @@ abstract class _UserModel extends UserModel { bool get isActive; @override bool get notifications; + + /// Create a copy of UserModel + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$UserModelImplCopyWith<_$UserModelImpl> get copyWith => throw _privateConstructorUsedError; } @@ -501,8 +514,12 @@ mixin _$ApiSession { DateTime? get created_at => throw _privateConstructorUsedError; bool get is_active => throw _privateConstructorUsedError; + /// Serializes this ApiSession to a JSON map. Map toJson() => throw _privateConstructorUsedError; - @JsonKey(ignore: true) + + /// Create a copy of ApiSession + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) $ApiSessionCopyWith get copyWith => throw _privateConstructorUsedError; } @@ -536,6 +553,8 @@ class _$ApiSessionCopyWithImpl<$Res, $Val extends ApiSession> // ignore: unused_field final $Res Function($Val) _then; + /// Create a copy of ApiSession + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -624,6 +643,8 @@ class __$$ApiSessionImplCopyWithImpl<$Res> _$ApiSessionImpl _value, $Res Function(_$ApiSessionImpl) _then) : super(_value, _then); + /// Create a copy of ApiSession + /// with the given fields replaced by the non-null parameter values. @pragma('vm:prefer-inline') @override $Res call({ @@ -754,7 +775,7 @@ class _$ApiSessionImpl extends _ApiSession { other.is_active == is_active)); } - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -769,7 +790,9 @@ class _$ApiSessionImpl extends _ApiSession { created_at, is_active); - @JsonKey(ignore: true) + /// Create a copy of ApiSession + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) @override @pragma('vm:prefer-inline') _$$ApiSessionImplCopyWith<_$ApiSessionImpl> get copyWith => @@ -820,8 +843,11 @@ abstract class _ApiSession extends ApiSession { DateTime? get created_at; @override bool get is_active; + + /// Create a copy of ApiSession + /// with the given fields replaced by the non-null parameter values. @override - @JsonKey(ignore: true) + @JsonKey(includeFromJson: false, includeToJson: false) _$$ApiSessionImplCopyWith<_$ApiSessionImpl> get copyWith => throw _privateConstructorUsedError; } diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index a16cf454..c559cc62 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -37,6 +37,17 @@ class MatchService { toFirestore: (MatchModel match, _) => match.toJson(), ); + DocumentReference _matchSettingDocument(String matchId) => + _firestore + .collection(FireStoreConst.matchesCollection) + .doc(matchId) + .collection(FireStoreConst.matchSettingsSubCollection) + .doc(FireStoreConst.settingDocument) + .withConverter( + fromFirestore: MatchSetting.fromFireStore, + toFirestore: (MatchSetting setting, _) => setting.toJson(), + ); + String get generateMatchId => _matchCollection.doc().id; Future getMatchById(String id) async { @@ -87,6 +98,19 @@ class MatchService { } } + Stream streamMatchSetting(String matchId) { + return _matchSettingDocument(matchId) + .snapshots() + .map((snapshot) => snapshot.data()) + .handleError((error, stack) => throw AppError.fromError(error, stack)); + } + + Future updateMatchSetting(String matchId, MatchSetting settings) async { + await _matchSettingDocument(matchId) + .set(settings, SetOptions(merge: true)) + .catchError((error, stack) => throw AppError.fromError(error, stack)); + } + Stream> streamUserRelatedMatches(String userId) { final filter = Filter.or( Filter(FireStoreConst.createdBy, isEqualTo: userId), diff --git a/data/lib/utils/constant/firestore_constant.dart b/data/lib/utils/constant/firestore_constant.dart index 76f9f142..278e4c96 100644 --- a/data/lib/utils/constant/firestore_constant.dart +++ b/data/lib/utils/constant/firestore_constant.dart @@ -1,6 +1,8 @@ class FireStoreConst { // collection static const String matchesCollection = "matches"; + static const String matchSettingsSubCollection = "match_settings"; + static const String settingDocument = "setting"; static const String teamsCollection = "teams"; static const String inningsCollection = "innings"; static const String ballScoresCollection = "ball_scores"; diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 40b5e33b..178e57a8 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -730,8 +730,8 @@ "score_board_penalty_run_title": "Penalty run", "score_board_awarded_to_text": "Awarded to - ?", "score_board_continue_with_injured_player_title": "Continue with injured player", - "score_board_not_show_for_less_run_title": "Don't show for 1s, 2s and 3s", - "score_board_not_show_for_dot_ball_title": "Don't show for dot balls", + "score_board_show_wheel_for_less_run_title": "Show wheel for 1s, 2s and 3s", + "score_board_show_wheel_for_dot_ball_title": "Show wheel for dot balls", "score_board_select_fielding_position_title": "Select fielding position", "score_board_pause_title": "Pause", "score_board_revised_target_title": "Revised target", diff --git a/khelo/lib/ui/flow/score_board/components/select_fielding_position_sheet.dart b/khelo/lib/ui/flow/score_board/components/select_fielding_position_sheet.dart index 1c174b16..949b6bb7 100644 --- a/khelo/lib/ui/flow/score_board/components/select_fielding_position_sheet.dart +++ b/khelo/lib/ui/flow/score_board/components/select_fielding_position_sheet.dart @@ -91,13 +91,13 @@ class _SelectFieldingPositionSheetState ), const SizedBox(height: 24), ToggleButtonTile( - title: context.l10n.score_board_not_show_for_less_run_title, + title: context.l10n.score_board_show_wheel_for_less_run_title, defaultEnabled: showForLessRun, onTap: (show) => showForLessRun = show, ), const SizedBox(height: 8), ToggleButtonTile( - title: context.l10n.score_board_not_show_for_dot_ball_title, + title: context.l10n.score_board_show_wheel_for_dot_ball_title, defaultEnabled: showForDotBall, onTap: (show) => showForDotBall = show, ), 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 ac75bef9..c39ef927 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 @@ -272,7 +272,8 @@ class _SelectPlayerSheetState extends ConsumerState { defaultEnabled: isEnabled, onTap: (value) => setState(() { isEnabled = !isEnabled; - notifier.onContinueWithInjuredPlayersChange(isEnabled); + notifier.onToggleMatchOptionChange( + isEnabled, MatchOption.continueWithInjuredPlayer); if (!isEnabled) { if (batsMan1?.performance .firstWhereOrNull( 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 d3ff7811..f75f0471 100644 --- a/khelo/lib/ui/flow/score_board/score_board_screen.dart +++ b/khelo/lib/ui/flow/score_board/score_board_screen.dart @@ -109,17 +109,17 @@ class _ScoreBoardScreenState extends ConsumerState { items: matchOptions .map((option) => BottomSheetAction( title: option.getTitle(context), + enabled: !option.showToggle(), onTap: () { - if (option != MatchOption.continueWithInjuredPlayer) { - context.pop(); - notifier.onMatchOptionSelect(option, true); - } + context.pop(); + notifier.onMatchOptionSelect(option, true); }, - child: option == MatchOption.continueWithInjuredPlayer + child: option.showToggle() ? toggleButton( context, - defaultEnabled: state.continueWithInjuredPlayers, - onTap: notifier.onContinueWithInjuredPlayersChange, + defaultEnabled: _getDefaultValue(state, option), + onTap: (result) => notifier + .onToggleMatchOptionChange(result, option), ) : null, )) @@ -127,6 +127,19 @@ class _ScoreBoardScreenState extends ConsumerState { ); } + bool _getDefaultValue(ScoreBoardViewState state, MatchOption option) { + switch (option) { + case MatchOption.continueWithInjuredPlayer: + return state.matchSetting.continue_with_injured_player; + case MatchOption.showForLessRuns: + return state.matchSetting.show_wagon_wheel_for_less_run; + case MatchOption.showForDotBall: + return state.matchSetting.show_wagon_wheel_for_dot_ball; + default: + return false; + } + } + Widget _body( BuildContext context, ScoreBoardViewState state, @@ -324,20 +337,20 @@ class _ScoreBoardScreenState extends ConsumerState { .select((value) => value.showSelectFieldingPositionSheet), (previous, next) async { if (next != null) { - final showForLessRun = ref.read( - scoreBoardStateProvider.select((value) => value.showForLessRun)); - final showForDotBall = ref.read( - scoreBoardStateProvider.select((value) => value.showForDotBall)); + final matchSetting = ref.read( + scoreBoardStateProvider.select((value) => value.matchSetting)); + final showForLessRun = matchSetting.show_wagon_wheel_for_less_run; + final showForDotBall = matchSetting.show_wagon_wheel_for_dot_ball; final tappedButton = ref.read( scoreBoardStateProvider.select((value) => value.tappedButton)); final isLongTapped = ref .read(scoreBoardStateProvider.select((value) => value.isLongTap)); final result = await SelectFieldingPositionSheet.show< (FieldingPositionType, bool, bool)>(context, - showForLessRun: !showForLessRun, showForDotBall: !showForDotBall); + showForLessRun: showForLessRun, showForDotBall: showForDotBall); if (context.mounted && result != null && tappedButton != null) { notifier.onFieldingPositionSelected(tappedButton, - isLongTapped ?? false, result.$1, !result.$2, !result.$3); + isLongTapped ?? false, result.$1, result.$2, result.$3); } } }); @@ -348,8 +361,9 @@ class _ScoreBoardScreenState extends ConsumerState { scoreBoardStateProvider.select((value) => value.showSelectBatsManSheet), (previous, next) { if (next != null) { - final continueWithInjuredPlayers = ref.read(scoreBoardStateProvider - .select((value) => value.continueWithInjuredPlayers)); + final continueWithInjuredPlayers = ref.read( + scoreBoardStateProvider.select( + (value) => value.matchSetting.continue_with_injured_player)); _showSelectPlayerSheet( context, continueWithInjuredPlayers, @@ -364,8 +378,9 @@ class _ScoreBoardScreenState extends ConsumerState { scoreBoardStateProvider.select((value) => value.showSelectBowlerSheet), (previous, next) { if (next != null) { - final continueWithInjuredPlayers = ref.read(scoreBoardStateProvider - .select((value) => value.continueWithInjuredPlayers)); + final continueWithInjuredPlayers = ref.read( + scoreBoardStateProvider.select( + (value) => value.matchSetting.continue_with_injured_player)); _showSelectPlayerSheet( context, continueWithInjuredPlayers, @@ -382,8 +397,9 @@ class _ScoreBoardScreenState extends ConsumerState { .select((value) => value.showSelectBowlerAndBatsManSheet), (previous, next) { if (next != null) { - final continueWithInjuredPlayers = ref.read(scoreBoardStateProvider - .select((value) => value.continueWithInjuredPlayers)); + final continueWithInjuredPlayers = ref.read( + scoreBoardStateProvider.select( + (value) => value.matchSetting.continue_with_injured_player)); _showSelectPlayerSheet( context, continueWithInjuredPlayers, @@ -398,8 +414,9 @@ class _ScoreBoardScreenState extends ConsumerState { scoreBoardStateProvider.select((value) => value.showSelectPlayerSheet), (previous, next) { if (next != null) { - final continueWithInjuredPlayers = ref.read(scoreBoardStateProvider - .select((value) => value.continueWithInjuredPlayers)); + final continueWithInjuredPlayers = ref.read( + scoreBoardStateProvider.select( + (value) => value.matchSetting.continue_with_injured_player)); _showSelectPlayerSheet( context, continueWithInjuredPlayers, 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 47216d85..3abf20a4 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 @@ -35,6 +35,7 @@ class ScoreBoardViewNotifier extends StateNotifier { final BallScoreService _ballScoreService; StreamSubscription? _matchStreamSubscription; StreamSubscription>? _ballScoreStreamSubscription; + StreamSubscription? _matchSettingSubscription; final StreamController _matchStreamController = StreamController.broadcast(); String? matchId; @@ -48,6 +49,18 @@ class ScoreBoardViewNotifier extends StateNotifier { void setData(String matchId) { this.matchId = matchId; _loadMatchesAndInning(); + _loadMatchSetting(); + } + + void _loadMatchSetting() { + if (matchId == null) return; + _matchSettingSubscription = + _matchService.streamMatchSetting(matchId!).listen((setting) { + state = state.copyWith(matchSetting: setting ?? state.matchSetting); + }, onError: (e) { + debugPrint( + "ScoreBoardViewNotifier: Error while loading match setting. $e"); + }); } Stream> _loadBallScoresStream( @@ -526,8 +539,8 @@ class ScoreBoardViewNotifier extends StateNotifier { tapped == ScoreButton.three; return (tapped != ScoreButton.undo && - ((isDotBall && state.showForDotBall) || - (isLessRuns && state.showForLessRun) || + ((isDotBall && state.matchSetting.show_wagon_wheel_for_dot_ball) || + (isLessRuns && state.matchSetting.show_wagon_wheel_for_less_run) || (!isDotBall && !isLessRuns))); } @@ -789,13 +802,15 @@ class ScoreBoardViewNotifier extends StateNotifier { } }); - final totalWicket = state.continueWithInjuredPlayers + final totalWicket = state.matchSetting.continue_with_injured_player ? (battingSquad?.length ?? 0) : (battingSquad?.length ?? 0) - injuredCount; return yetToPlayCount == 0 && playingCount == 1 && state.otherInning!.total_wickets == totalWicket - 1 && - (state.continueWithInjuredPlayers ? injuredCount == 0 : true); + (state.matchSetting.continue_with_injured_player + ? injuredCount == 0 + : true); } bool _isAllDeliveryDelivered() { @@ -1117,7 +1132,6 @@ class ScoreBoardViewNotifier extends StateNotifier { previousScoresList: runningInning.index == 3 ? List.empty() : state.currentScoresList, currentScoresList: List.empty(), - continueWithInjuredPlayers: true, showSelectPlayerSheet: DateTime.now(), ballScoreQueryListenerSet: runningInning.index == 3 ? false @@ -1209,8 +1223,36 @@ class ScoreBoardViewNotifier extends StateNotifier { } } - void onContinueWithInjuredPlayersChange(bool isContinue) { - state = state.copyWith(continueWithInjuredPlayers: isContinue); + void onToggleMatchOptionChange(bool isTrue, MatchOption option) { + MatchSetting setting = state.matchSetting; + switch (option) { + case MatchOption.continueWithInjuredPlayer: + setting = setting.copyWith(continue_with_injured_player: isTrue); + break; + case MatchOption.showForLessRuns: + setting = setting.copyWith(show_wagon_wheel_for_less_run: isTrue); + break; + case MatchOption.showForDotBall: + setting = setting.copyWith(show_wagon_wheel_for_dot_ball: isTrue); + break; + default: + return; + } + _updateMatchSetting(setting); + } + + Future _updateMatchSetting(MatchSetting setting) async { + final matchId = this.matchId ?? state.match?.id; + if (matchId == null) return; + state = state.copyWith(actionError: null); + try { + await _matchService.updateMatchSetting(matchId, setting); + state = state.copyWith(matchSetting: setting); + } catch (e) { + state = state.copyWith(actionError: e); + debugPrint( + "ScoreBoardViewNotifier: Error while update match setting -> $e"); + } } Future setPlayers({ @@ -1459,17 +1501,19 @@ class ScoreBoardViewNotifier extends StateNotifier { return teamPlayers ?? []; } - void onFieldingPositionSelected( + Future onFieldingPositionSelected( ScoreButton tapped, bool isLongTapped, FieldingPositionType position, bool showForLessRun, bool showForDotBall, - ) { - state = state.copyWith( - position: position, - showForDotBall: showForDotBall, - showForLessRun: showForLessRun); + ) async { + final setting = state.matchSetting.copyWith( + show_wagon_wheel_for_less_run: showForLessRun, + show_wagon_wheel_for_dot_ball: showForDotBall, + ); + await _updateMatchSetting(setting); + state = state.copyWith(position: position); _handleAddingBall(tapped, isLongTapped); } @@ -1497,15 +1541,17 @@ class ScoreBoardViewNotifier extends StateNotifier { matchId: matchId, revisedTarget: revisedTarget); } - _cancelStreamSubscription() async { + _cancelStreamSubscription() async { await _matchStreamSubscription?.cancel(); await _ballScoreStreamSubscription?.cancel(); + await _matchSettingSubscription?.cancel(); await _matchStreamController.close(); } void onResume() { _cancelStreamSubscription(); _loadMatchesAndInning(); + _loadMatchSetting(); } @override @@ -1554,14 +1600,12 @@ class ScoreBoardViewState with _$ScoreBoardViewState { @Default([]) List allInnings, @Default([]) List currentScoresList, @Default([]) List previousScoresList, + @Default(MatchSetting()) MatchSetting matchSetting, @Default(false) bool loading, @Default(false) bool pop, - @Default(true) bool continueWithInjuredPlayers, @Default(false) bool ballScoreQueryListenerSet, @Default(true) bool isMatchUpdated, @Default(false) bool isActionInProgress, - @Default(true) bool showForLessRun, - @Default(true) bool showForDotBall, @Default(0) int ballCount, @Default(1) int overCount, @Default(0) int lastAssignedIndex, @@ -1621,6 +1665,8 @@ enum MatchOption { reviseTarget, pauseScoring, continueWithInjuredPlayer, + showForLessRuns, + showForDotBall, addSubstitute, endMatch; @@ -1640,6 +1686,21 @@ enum MatchOption { return context.l10n.score_board_add_substitute_title; case MatchOption.endMatch: return context.l10n.score_board_option_end_match; + case MatchOption.showForLessRuns: + return context.l10n.score_board_show_wheel_for_less_run_title; + case MatchOption.showForDotBall: + return context.l10n.score_board_show_wheel_for_dot_ball_title; + } + } + + bool showToggle() { + switch (this) { + case MatchOption.continueWithInjuredPlayer: + case MatchOption.showForLessRuns: + case MatchOption.showForDotBall: + return true; + default: + return false; } } } 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 aed8c671..0c3cc148 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 @@ -60,14 +60,12 @@ mixin _$ScoreBoardViewState { throw _privateConstructorUsedError; List get previousScoresList => throw _privateConstructorUsedError; + MatchSetting get matchSetting => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; bool get pop => throw _privateConstructorUsedError; - bool get continueWithInjuredPlayers => throw _privateConstructorUsedError; bool get ballScoreQueryListenerSet => throw _privateConstructorUsedError; bool get isMatchUpdated => throw _privateConstructorUsedError; bool get isActionInProgress => throw _privateConstructorUsedError; - bool get showForLessRun => throw _privateConstructorUsedError; - bool get showForDotBall => throw _privateConstructorUsedError; int get ballCount => throw _privateConstructorUsedError; int get overCount => throw _privateConstructorUsedError; int get lastAssignedIndex => throw _privateConstructorUsedError; @@ -122,14 +120,12 @@ abstract class $ScoreBoardViewStateCopyWith<$Res> { List allInnings, List currentScoresList, List previousScoresList, + MatchSetting matchSetting, bool loading, bool pop, - bool continueWithInjuredPlayers, bool ballScoreQueryListenerSet, bool isMatchUpdated, bool isActionInProgress, - bool showForLessRun, - bool showForDotBall, int ballCount, int overCount, int lastAssignedIndex}); @@ -139,6 +135,7 @@ abstract class $ScoreBoardViewStateCopyWith<$Res> { $InningModelCopyWith<$Res>? get otherInning; $MatchPlayerCopyWith<$Res>? get bowler; $InningModelCopyWith<$Res>? get nextInning; + $MatchSettingCopyWith<$Res> get matchSetting; } /// @nodoc @@ -192,14 +189,12 @@ class _$ScoreBoardViewStateCopyWithImpl<$Res, $Val extends ScoreBoardViewState> Object? allInnings = null, Object? currentScoresList = null, Object? previousScoresList = null, + Object? matchSetting = null, Object? loading = null, Object? pop = null, - Object? continueWithInjuredPlayers = null, Object? ballScoreQueryListenerSet = null, Object? isMatchUpdated = null, Object? isActionInProgress = null, - Object? showForLessRun = null, - Object? showForDotBall = null, Object? ballCount = null, Object? overCount = null, Object? lastAssignedIndex = null, @@ -345,6 +340,10 @@ class _$ScoreBoardViewStateCopyWithImpl<$Res, $Val extends ScoreBoardViewState> ? _value.previousScoresList : previousScoresList // ignore: cast_nullable_to_non_nullable as List, + matchSetting: null == matchSetting + ? _value.matchSetting + : matchSetting // ignore: cast_nullable_to_non_nullable + as MatchSetting, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable @@ -353,10 +352,6 @@ class _$ScoreBoardViewStateCopyWithImpl<$Res, $Val extends ScoreBoardViewState> ? _value.pop : pop // ignore: cast_nullable_to_non_nullable as bool, - continueWithInjuredPlayers: null == continueWithInjuredPlayers - ? _value.continueWithInjuredPlayers - : continueWithInjuredPlayers // ignore: cast_nullable_to_non_nullable - as bool, ballScoreQueryListenerSet: null == ballScoreQueryListenerSet ? _value.ballScoreQueryListenerSet : ballScoreQueryListenerSet // ignore: cast_nullable_to_non_nullable @@ -369,14 +364,6 @@ class _$ScoreBoardViewStateCopyWithImpl<$Res, $Val extends ScoreBoardViewState> ? _value.isActionInProgress : isActionInProgress // ignore: cast_nullable_to_non_nullable as bool, - showForLessRun: null == showForLessRun - ? _value.showForLessRun - : showForLessRun // ignore: cast_nullable_to_non_nullable - as bool, - showForDotBall: null == showForDotBall - ? _value.showForDotBall - : showForDotBall // ignore: cast_nullable_to_non_nullable - as bool, ballCount: null == ballCount ? _value.ballCount : ballCount // ignore: cast_nullable_to_non_nullable @@ -461,6 +448,16 @@ class _$ScoreBoardViewStateCopyWithImpl<$Res, $Val extends ScoreBoardViewState> return _then(_value.copyWith(nextInning: value) as $Val); }); } + + /// Create a copy of ScoreBoardViewState + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $MatchSettingCopyWith<$Res> get matchSetting { + return $MatchSettingCopyWith<$Res>(_value.matchSetting, (value) { + return _then(_value.copyWith(matchSetting: value) as $Val); + }); + } } /// @nodoc @@ -508,14 +505,12 @@ abstract class _$$ScoreBoardViewStateImplCopyWith<$Res> List allInnings, List currentScoresList, List previousScoresList, + MatchSetting matchSetting, bool loading, bool pop, - bool continueWithInjuredPlayers, bool ballScoreQueryListenerSet, bool isMatchUpdated, bool isActionInProgress, - bool showForLessRun, - bool showForDotBall, int ballCount, int overCount, int lastAssignedIndex}); @@ -530,6 +525,8 @@ abstract class _$$ScoreBoardViewStateImplCopyWith<$Res> $MatchPlayerCopyWith<$Res>? get bowler; @override $InningModelCopyWith<$Res>? get nextInning; + @override + $MatchSettingCopyWith<$Res> get matchSetting; } /// @nodoc @@ -581,14 +578,12 @@ class __$$ScoreBoardViewStateImplCopyWithImpl<$Res> Object? allInnings = null, Object? currentScoresList = null, Object? previousScoresList = null, + Object? matchSetting = null, Object? loading = null, Object? pop = null, - Object? continueWithInjuredPlayers = null, Object? ballScoreQueryListenerSet = null, Object? isMatchUpdated = null, Object? isActionInProgress = null, - Object? showForLessRun = null, - Object? showForDotBall = null, Object? ballCount = null, Object? overCount = null, Object? lastAssignedIndex = null, @@ -734,6 +729,10 @@ class __$$ScoreBoardViewStateImplCopyWithImpl<$Res> ? _value._previousScoresList : previousScoresList // ignore: cast_nullable_to_non_nullable as List, + matchSetting: null == matchSetting + ? _value.matchSetting + : matchSetting // ignore: cast_nullable_to_non_nullable + as MatchSetting, loading: null == loading ? _value.loading : loading // ignore: cast_nullable_to_non_nullable @@ -742,10 +741,6 @@ class __$$ScoreBoardViewStateImplCopyWithImpl<$Res> ? _value.pop : pop // ignore: cast_nullable_to_non_nullable as bool, - continueWithInjuredPlayers: null == continueWithInjuredPlayers - ? _value.continueWithInjuredPlayers - : continueWithInjuredPlayers // ignore: cast_nullable_to_non_nullable - as bool, ballScoreQueryListenerSet: null == ballScoreQueryListenerSet ? _value.ballScoreQueryListenerSet : ballScoreQueryListenerSet // ignore: cast_nullable_to_non_nullable @@ -758,14 +753,6 @@ class __$$ScoreBoardViewStateImplCopyWithImpl<$Res> ? _value.isActionInProgress : isActionInProgress // ignore: cast_nullable_to_non_nullable as bool, - showForLessRun: null == showForLessRun - ? _value.showForLessRun - : showForLessRun // ignore: cast_nullable_to_non_nullable - as bool, - showForDotBall: null == showForDotBall - ? _value.showForDotBall - : showForDotBall // ignore: cast_nullable_to_non_nullable - as bool, ballCount: null == ballCount ? _value.ballCount : ballCount // ignore: cast_nullable_to_non_nullable @@ -822,14 +809,12 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { final List allInnings = const [], final List currentScoresList = const [], final List previousScoresList = const [], + this.matchSetting = const MatchSetting(), this.loading = false, this.pop = false, - this.continueWithInjuredPlayers = true, this.ballScoreQueryListenerSet = false, this.isMatchUpdated = true, this.isActionInProgress = false, - this.showForLessRun = true, - this.showForDotBall = true, this.ballCount = 0, this.overCount = 1, this.lastAssignedIndex = 0}) @@ -943,13 +928,13 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { @override @JsonKey() - final bool loading; + final MatchSetting matchSetting; @override @JsonKey() - final bool pop; + final bool loading; @override @JsonKey() - final bool continueWithInjuredPlayers; + final bool pop; @override @JsonKey() final bool ballScoreQueryListenerSet; @@ -961,12 +946,6 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { final bool isActionInProgress; @override @JsonKey() - final bool showForLessRun; - @override - @JsonKey() - final bool showForDotBall; - @override - @JsonKey() final int ballCount; @override @JsonKey() @@ -977,7 +956,7 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { @override String toString() { - return 'ScoreBoardViewState(error: $error, actionError: $actionError, match: $match, currentInning: $currentInning, otherInning: $otherInning, bowler: $bowler, strikerId: $strikerId, batsMans: $batsMans, nextInning: $nextInning, showSelectFieldingPositionSheet: $showSelectFieldingPositionSheet, showSelectBatsManSheet: $showSelectBatsManSheet, showSelectBowlerSheet: $showSelectBowlerSheet, showSelectBowlerAndBatsManSheet: $showSelectBowlerAndBatsManSheet, showSelectPlayerSheet: $showSelectPlayerSheet, showSelectWicketTypeSheet: $showSelectWicketTypeSheet, showStrikerSelectionSheet: $showStrikerSelectionSheet, showUndoBallConfirmationDialog: $showUndoBallConfirmationDialog, showOverCompleteSheet: $showOverCompleteSheet, showInningCompleteSheet: $showInningCompleteSheet, showMatchCompleteSheet: $showMatchCompleteSheet, showAddExtraSheetForNoBall: $showAddExtraSheetForNoBall, showAddExtraSheetForLegBye: $showAddExtraSheetForLegBye, showAddExtraSheetForBye: $showAddExtraSheetForBye, showAddExtraSheetForFiveSeven: $showAddExtraSheetForFiveSeven, showPauseScoringSheet: $showPauseScoringSheet, showAddPenaltyRunSheet: $showAddPenaltyRunSheet, showEndMatchSheet: $showEndMatchSheet, showAddSubstituteSheet: $showAddSubstituteSheet, invalidUndoToast: $invalidUndoToast, showReviseTargetSheet: $showReviseTargetSheet, tappedButton: $tappedButton, isLongTap: $isLongTap, position: $position, allInnings: $allInnings, currentScoresList: $currentScoresList, previousScoresList: $previousScoresList, loading: $loading, pop: $pop, continueWithInjuredPlayers: $continueWithInjuredPlayers, ballScoreQueryListenerSet: $ballScoreQueryListenerSet, isMatchUpdated: $isMatchUpdated, isActionInProgress: $isActionInProgress, showForLessRun: $showForLessRun, showForDotBall: $showForDotBall, ballCount: $ballCount, overCount: $overCount, lastAssignedIndex: $lastAssignedIndex)'; + return 'ScoreBoardViewState(error: $error, actionError: $actionError, match: $match, currentInning: $currentInning, otherInning: $otherInning, bowler: $bowler, strikerId: $strikerId, batsMans: $batsMans, nextInning: $nextInning, showSelectFieldingPositionSheet: $showSelectFieldingPositionSheet, showSelectBatsManSheet: $showSelectBatsManSheet, showSelectBowlerSheet: $showSelectBowlerSheet, showSelectBowlerAndBatsManSheet: $showSelectBowlerAndBatsManSheet, showSelectPlayerSheet: $showSelectPlayerSheet, showSelectWicketTypeSheet: $showSelectWicketTypeSheet, showStrikerSelectionSheet: $showStrikerSelectionSheet, showUndoBallConfirmationDialog: $showUndoBallConfirmationDialog, showOverCompleteSheet: $showOverCompleteSheet, showInningCompleteSheet: $showInningCompleteSheet, showMatchCompleteSheet: $showMatchCompleteSheet, showAddExtraSheetForNoBall: $showAddExtraSheetForNoBall, showAddExtraSheetForLegBye: $showAddExtraSheetForLegBye, showAddExtraSheetForBye: $showAddExtraSheetForBye, showAddExtraSheetForFiveSeven: $showAddExtraSheetForFiveSeven, showPauseScoringSheet: $showPauseScoringSheet, showAddPenaltyRunSheet: $showAddPenaltyRunSheet, showEndMatchSheet: $showEndMatchSheet, showAddSubstituteSheet: $showAddSubstituteSheet, invalidUndoToast: $invalidUndoToast, showReviseTargetSheet: $showReviseTargetSheet, tappedButton: $tappedButton, isLongTap: $isLongTap, position: $position, allInnings: $allInnings, currentScoresList: $currentScoresList, previousScoresList: $previousScoresList, matchSetting: $matchSetting, loading: $loading, pop: $pop, ballScoreQueryListenerSet: $ballScoreQueryListenerSet, isMatchUpdated: $isMatchUpdated, isActionInProgress: $isActionInProgress, ballCount: $ballCount, overCount: $overCount, lastAssignedIndex: $lastAssignedIndex)'; } @override @@ -1048,14 +1027,12 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { const DeepCollectionEquality().equals(other._allInnings, _allInnings) && const DeepCollectionEquality().equals(other._currentScoresList, _currentScoresList) && const DeepCollectionEquality().equals(other._previousScoresList, _previousScoresList) && + (identical(other.matchSetting, matchSetting) || other.matchSetting == matchSetting) && (identical(other.loading, loading) || other.loading == loading) && (identical(other.pop, pop) || other.pop == pop) && - (identical(other.continueWithInjuredPlayers, continueWithInjuredPlayers) || other.continueWithInjuredPlayers == continueWithInjuredPlayers) && (identical(other.ballScoreQueryListenerSet, ballScoreQueryListenerSet) || other.ballScoreQueryListenerSet == ballScoreQueryListenerSet) && (identical(other.isMatchUpdated, isMatchUpdated) || other.isMatchUpdated == isMatchUpdated) && (identical(other.isActionInProgress, isActionInProgress) || other.isActionInProgress == isActionInProgress) && - (identical(other.showForLessRun, showForLessRun) || other.showForLessRun == showForLessRun) && - (identical(other.showForDotBall, showForDotBall) || other.showForDotBall == showForDotBall) && (identical(other.ballCount, ballCount) || other.ballCount == ballCount) && (identical(other.overCount, overCount) || other.overCount == overCount) && (identical(other.lastAssignedIndex, lastAssignedIndex) || other.lastAssignedIndex == lastAssignedIndex)); @@ -1100,14 +1077,12 @@ class _$ScoreBoardViewStateImpl implements _ScoreBoardViewState { const DeepCollectionEquality().hash(_allInnings), const DeepCollectionEquality().hash(_currentScoresList), const DeepCollectionEquality().hash(_previousScoresList), + matchSetting, loading, pop, - continueWithInjuredPlayers, ballScoreQueryListenerSet, isMatchUpdated, isActionInProgress, - showForLessRun, - showForDotBall, ballCount, overCount, lastAssignedIndex @@ -1161,14 +1136,12 @@ abstract class _ScoreBoardViewState implements ScoreBoardViewState { final List allInnings, final List currentScoresList, final List previousScoresList, + final MatchSetting matchSetting, final bool loading, final bool pop, - final bool continueWithInjuredPlayers, final bool ballScoreQueryListenerSet, final bool isMatchUpdated, final bool isActionInProgress, - final bool showForLessRun, - final bool showForDotBall, final int ballCount, final int overCount, final int lastAssignedIndex}) = _$ScoreBoardViewStateImpl; @@ -1246,22 +1219,18 @@ abstract class _ScoreBoardViewState implements ScoreBoardViewState { @override List get previousScoresList; @override + MatchSetting get matchSetting; + @override bool get loading; @override bool get pop; @override - bool get continueWithInjuredPlayers; - @override bool get ballScoreQueryListenerSet; @override bool get isMatchUpdated; @override bool get isActionInProgress; @override - bool get showForLessRun; - @override - bool get showForDotBall; - @override int get ballCount; @override int get overCount; From 1d51d72bbf06f7ba4e0f4ff4e5e58ef007caade3 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 11 Oct 2024 17:17:31 +0530 Subject: [PATCH 17/33] Add tabs --- khelo/assets/locales/app_en.arb | 5 + .../components/sliver_header_delegate.dart | 32 ++++ .../detail/tournament_detail_screen.dart | 139 +++++++++++++----- .../detail/tournament_detail_view_model.dart | 12 ++ .../tournament_detail_view_model.freezed.dart | 40 ++++- .../tournament/tournament_list_screen.dart | 33 +---- 6 files changed, 190 insertions(+), 71 deletions(-) create mode 100644 khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 4506adf3..5fb4164b 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -177,6 +177,11 @@ } } }, + "tournament_detail_overview_tab": "Overview", + "tournament_detail_teams_tab": "Teams", + "tournament_detail_matches_tab": "Matches", + "tournament_detail_points_table_tab": "Points Table", + "tournament_detail_stats_tab": "Stats", "@_TOURNAMENT_TYPE":{ }, diff --git a/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart b/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart new file mode 100644 index 00000000..cc96330b --- /dev/null +++ b/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:style/extensions/context_extensions.dart'; + +class SliverPersistentDelegate extends SliverPersistentHeaderDelegate { + final Widget child; + + SliverPersistentDelegate({Key? key, required this.child}); + + @override + Widget build( + BuildContext context, + double shrinkOffset, + bool overlapsContent, + ) { + return Container( + color: context.colorScheme.surface, + alignment: Alignment.centerLeft, + child: child, + ); + } + + @override + bool shouldRebuild(SliverPersistentDelegate oldDelegate) { + return child != oldDelegate.child; + } + + @override + double get maxExtent => 50; + + @override + double get minExtent => 50; +} diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index 4624b9de..a14ba613 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -7,7 +7,10 @@ import 'package:khelo/components/empty_screen.dart'; import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/formatter/date_formatter.dart'; +import 'package:khelo/ui/flow/tournament/components/sliver_header_delegate.dart'; import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart'; +import 'package:style/button/more_option_button.dart'; +import 'package:style/button/tab_button.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/indicator/progress_indicator.dart'; import 'package:style/text/app_text_style.dart'; @@ -33,11 +36,19 @@ class TournamentDetailScreen extends ConsumerStatefulWidget { class _TournamentDetailScreenState extends ConsumerState { late TournamentDetailStateViewNotifier notifier; + late PageController _controller; + + int get _selectedTab => _controller.hasClients + ? _controller.page?.round() ?? 0 + : _controller.initialPage; @override void initState() { super.initState(); notifier = ref.read(tournamentDetailStateProvider.notifier); + _controller = PageController( + initialPage: ref.read(tournamentDetailStateProvider).selectedTab, + ); runPostFrame(() => notifier.setData(widget.tournamentId)); } @@ -73,16 +84,24 @@ class _TournamentDetailScreenState return CustomScrollView( slivers: [ SliverAppBar( + pinned: true, expandedHeight: 300, backgroundColor: context.colorScheme.surface, - pinned: true, flexibleSpace: _flexibleTitle(context, state.tournament!), - actions: [], + actions: [ + moreOptionButton(context), + ], ), SliverToBoxAdapter( child: SizedBox(height: 16 + context.mediaQueryPadding.bottom), ), - SliverToBoxAdapter( + SliverPersistentHeader( + pinned: true, + delegate: SliverPersistentDelegate( + child: _tabSelection(context), + ), + ), + SliverFillRemaining( child: _content(context, state), ) ], @@ -90,46 +109,96 @@ class _TournamentDetailScreenState } Widget _content(BuildContext context, TournamentDetailState state) { - return Column(); + return PageView( + controller: _controller, + onPageChanged: notifier.onTabChange, + children: const [ + //Add Tab view + ], + ); } - Widget _flexibleTitle(BuildContext context, TournamentModel tournament) { - return FlexibleSpaceBar( - background: Stack( - children: [ - Container( - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - image: (tournament.banner_img_url != null) - ? DecorationImage( - image: CachedNetworkImageProvider( - tournament.banner_img_url ?? ''), - fit: BoxFit.fill, - ) - : null, + Widget _tabSelection(BuildContext context) { + final tabs = [ + context.l10n.tournament_detail_overview_tab, + context.l10n.tournament_detail_teams_tab, + context.l10n.tournament_detail_matches_tab, + context.l10n.tournament_detail_points_table_tab, + context.l10n.tournament_detail_stats_tab, + ]; + + return SingleChildScrollView( + padding: const EdgeInsets.symmetric(horizontal: 12), + scrollDirection: Axis.horizontal, + child: Row( + children: List.generate( + tabs.length, + (index) => Padding( + padding: const EdgeInsets.symmetric(horizontal: 4), + child: TabButton( + tabs[index], + selected: index == _selectedTab, + onTap: () { + _controller.jumpToPage(index); + }, ), - child: (tournament.banner_img_url == null) - ? Center( - child: SvgPicture.asset( - Assets.images.icTournaments, - colorFilter: ColorFilter.mode( - context.colorScheme.textPrimary, - BlendMode.srcIn, - ), - ), - ) - : null, ), - Positioned( - left: 16, - bottom: 24, - child: _profileView(context, tournament), - ), - ], + ), ), ); } + Widget _flexibleTitle(BuildContext context, TournamentModel tournament) { + return LayoutBuilder( + builder: (BuildContext context, BoxConstraints constraints) { + final isCollapsed = constraints.biggest.height < 150; + + return FlexibleSpaceBar( + title: isCollapsed + ? Text( + tournament.name, + style: AppTextStyle.header2.copyWith( + color: context.colorScheme.textPrimary, + ), + ) + : null, + background: Stack( + children: [ + Container( + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + image: (tournament.banner_img_url != null) + ? DecorationImage( + image: CachedNetworkImageProvider( + tournament.banner_img_url ?? ''), + fit: BoxFit.fill, + ) + : null, + ), + child: (tournament.banner_img_url == null) + ? Center( + child: SvgPicture.asset( + Assets.images.icTournaments, + colorFilter: ColorFilter.mode( + context.colorScheme.textPrimary, + BlendMode.srcIn, + ), + ), + ) + : null, + ), + if (!isCollapsed) + Positioned( + left: 16, + bottom: 24, + child: _profileView(context, tournament), + ), + ], + ), + ); + }); + } + Widget _profileView(BuildContext context, TournamentModel tournament) { return Column( crossAxisAlignment: CrossAxisAlignment.start, diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart index ced65490..3969801b 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -45,6 +45,17 @@ class TournamentDetailStateViewNotifier "TournamentListViewNotifier: error while loading tournament list -> $e"); }); } + + void onTabChange(int tab) { + if (state.selectedTab != tab) { + state = state.copyWith(selectedTab: tab); + } + } + @override + void dispose() { + _tournamentSubscription?.cancel(); + super.dispose(); + } } @freezed @@ -52,6 +63,7 @@ class TournamentDetailState with _$TournamentDetailState { const factory TournamentDetailState({ @Default(null) TournamentModel? tournament, @Default(false) bool loading, + @Default(0) int selectedTab, Object? error, }) = _TournamentDetailState; } diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart index 2ee503e2..cfeb2014 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart @@ -18,6 +18,7 @@ final _privateConstructorUsedError = UnsupportedError( mixin _$TournamentDetailState { TournamentModel? get tournament => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; + int get selectedTab => throw _privateConstructorUsedError; Object? get error => throw _privateConstructorUsedError; /// Create a copy of TournamentDetailState @@ -33,7 +34,11 @@ abstract class $TournamentDetailStateCopyWith<$Res> { $Res Function(TournamentDetailState) then) = _$TournamentDetailStateCopyWithImpl<$Res, TournamentDetailState>; @useResult - $Res call({TournamentModel? tournament, bool loading, Object? error}); + $Res call( + {TournamentModel? tournament, + bool loading, + int selectedTab, + Object? error}); $TournamentModelCopyWith<$Res>? get tournament; } @@ -56,6 +61,7 @@ class _$TournamentDetailStateCopyWithImpl<$Res, $Res call({ Object? tournament = freezed, Object? loading = null, + Object? selectedTab = null, Object? error = freezed, }) { return _then(_value.copyWith( @@ -67,6 +73,10 @@ class _$TournamentDetailStateCopyWithImpl<$Res, ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + selectedTab: null == selectedTab + ? _value.selectedTab + : selectedTab // ignore: cast_nullable_to_non_nullable + as int, error: freezed == error ? _value.error : error, ) as $Val); } @@ -95,7 +105,11 @@ abstract class _$$TournamentDetailStateImplCopyWith<$Res> __$$TournamentDetailStateImplCopyWithImpl<$Res>; @override @useResult - $Res call({TournamentModel? tournament, bool loading, Object? error}); + $Res call( + {TournamentModel? tournament, + bool loading, + int selectedTab, + Object? error}); @override $TournamentModelCopyWith<$Res>? get tournament; @@ -117,6 +131,7 @@ class __$$TournamentDetailStateImplCopyWithImpl<$Res> $Res call({ Object? tournament = freezed, Object? loading = null, + Object? selectedTab = null, Object? error = freezed, }) { return _then(_$TournamentDetailStateImpl( @@ -128,6 +143,10 @@ class __$$TournamentDetailStateImplCopyWithImpl<$Res> ? _value.loading : loading // ignore: cast_nullable_to_non_nullable as bool, + selectedTab: null == selectedTab + ? _value.selectedTab + : selectedTab // ignore: cast_nullable_to_non_nullable + as int, error: freezed == error ? _value.error : error, )); } @@ -137,7 +156,10 @@ class __$$TournamentDetailStateImplCopyWithImpl<$Res> class _$TournamentDetailStateImpl implements _TournamentDetailState { const _$TournamentDetailStateImpl( - {this.tournament = null, this.loading = false, this.error}); + {this.tournament = null, + this.loading = false, + this.selectedTab = 0, + this.error}); @override @JsonKey() @@ -146,11 +168,14 @@ class _$TournamentDetailStateImpl implements _TournamentDetailState { @JsonKey() final bool loading; @override + @JsonKey() + final int selectedTab; + @override final Object? error; @override String toString() { - return 'TournamentDetailState(tournament: $tournament, loading: $loading, error: $error)'; + return 'TournamentDetailState(tournament: $tournament, loading: $loading, selectedTab: $selectedTab, error: $error)'; } @override @@ -161,11 +186,13 @@ class _$TournamentDetailStateImpl implements _TournamentDetailState { (identical(other.tournament, tournament) || other.tournament == tournament) && (identical(other.loading, loading) || other.loading == loading) && + (identical(other.selectedTab, selectedTab) || + other.selectedTab == selectedTab) && const DeepCollectionEquality().equals(other.error, error)); } @override - int get hashCode => Object.hash(runtimeType, tournament, loading, + int get hashCode => Object.hash(runtimeType, tournament, loading, selectedTab, const DeepCollectionEquality().hash(error)); /// Create a copy of TournamentDetailState @@ -182,6 +209,7 @@ abstract class _TournamentDetailState implements TournamentDetailState { const factory _TournamentDetailState( {final TournamentModel? tournament, final bool loading, + final int selectedTab, final Object? error}) = _$TournamentDetailStateImpl; @override @@ -189,6 +217,8 @@ abstract class _TournamentDetailState implements TournamentDetailState { @override bool get loading; @override + int get selectedTab; + @override Object? get error; /// Create a copy of TournamentDetailState diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index 85c43c86..e8b6ed53 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -17,6 +17,7 @@ import 'package:style/text/app_text_style.dart'; import '../../../components/empty_screen.dart'; import '../../../components/error_screen.dart'; import '../../../gen/assets.gen.dart'; +import 'components/sliver_header_delegate.dart'; class TournamentListScreen extends ConsumerStatefulWidget { const TournamentListScreen({super.key}); @@ -97,7 +98,7 @@ class _TournamentListScreenState extends ConsumerState slivers: [ SliverPersistentHeader( pinned: true, - delegate: SliverAppbarDelegate( + delegate: SliverPersistentDelegate( child: Padding( padding: const EdgeInsets.symmetric(horizontal: 16), child: Text( @@ -228,33 +229,3 @@ class _TournamentListScreenState extends ConsumerState ])); } } - -class SliverAppbarDelegate extends SliverPersistentHeaderDelegate { - final Widget child; - - SliverAppbarDelegate({Key? key, required this.child}); - - @override - Widget build( - BuildContext context, - double shrinkOffset, - bool overlapsContent, - ) { - return Container( - color: context.colorScheme.surface, - alignment: Alignment.centerLeft, - child: child, - ); - } - - @override - bool shouldRebuild(SliverAppbarDelegate oldDelegate) { - return child != oldDelegate.child; - } - - @override - double get maxExtent => 50; - - @override - double get minExtent => 50; -} From 2697e5c6f0338069f4521c207c4fc8fa47b66c8b Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 11 Oct 2024 17:30:04 +0530 Subject: [PATCH 18/33] minor changes --- .../components/sliver_header_delegate.dart | 11 +++++--- .../detail/tournament_detail_screen.dart | 27 +++++++++++-------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart b/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart index cc96330b..495a47e5 100644 --- a/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart +++ b/khelo/lib/ui/flow/tournament/components/sliver_header_delegate.dart @@ -3,8 +3,13 @@ import 'package:style/extensions/context_extensions.dart'; class SliverPersistentDelegate extends SliverPersistentHeaderDelegate { final Widget child; + final double size; - SliverPersistentDelegate({Key? key, required this.child}); + SliverPersistentDelegate({ + Key? key, + required this.child, + this.size = 50, + }); @override Widget build( @@ -25,8 +30,8 @@ class SliverPersistentDelegate extends SliverPersistentHeaderDelegate { } @override - double get maxExtent => 50; + double get maxExtent => size; @override - double get minExtent => 50; + double get minExtent => size; } diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index a14ba613..1d999d02 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -92,13 +92,11 @@ class _TournamentDetailScreenState moreOptionButton(context), ], ), - SliverToBoxAdapter( - child: SizedBox(height: 16 + context.mediaQueryPadding.bottom), - ), SliverPersistentHeader( pinned: true, delegate: SliverPersistentDelegate( child: _tabSelection(context), + size: 60, ), ), SliverFillRemaining( @@ -155,10 +153,14 @@ class _TournamentDetailScreenState return FlexibleSpaceBar( title: isCollapsed - ? Text( - tournament.name, - style: AppTextStyle.header2.copyWith( - color: context.colorScheme.textPrimary, + ? AnimatedOpacity( + opacity: isCollapsed ? 1 : 0, + duration: const Duration(milliseconds: 100), + child: Text( + tournament.name, + style: AppTextStyle.header2.copyWith( + color: context.colorScheme.textPrimary, + ), ), ) : null, @@ -187,12 +189,15 @@ class _TournamentDetailScreenState ) : null, ), - if (!isCollapsed) - Positioned( - left: 16, - bottom: 24, + Positioned( + left: 16, + bottom: 24, + child: AnimatedOpacity( + opacity: isCollapsed ? 0 : 1, + duration: const Duration(milliseconds: 100), child: _profileView(context, tournament), ), + ), ], ), ); From 56af43c1eec4880cb9ffe999284e9fffe7d50d43 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 11 Oct 2024 18:46:55 +0530 Subject: [PATCH 19/33] minor changes --- data/lib/service/match/match_service.dart | 11 ++---- data/lib/service/team/team_service.dart | 3 +- .../tournament/tournament_service.dart | 38 +++++++++---------- khelo/assets/locales/app_en.arb | 2 +- .../detail/tournament_detail_view_model.dart | 2 +- 5 files changed, 26 insertions(+), 30 deletions(-) diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index cbd227ef..11b5dae7 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -465,15 +465,12 @@ class MatchService { } } - //Helper Methods Future> getMatchesByIds(List matchIds) async { try { - final List matches = []; - await Future.forEach(matchIds, (matchId) async { - final match = await getMatchById(matchId); - matches.add(match); - }); - return matches; + return await _matchCollection + .where(FieldPath.documentId, whereIn: matchIds) + .get() + .then((value) => value.docs.map((e) => e.data()).toList()); } catch (error, stack) { throw AppError.fromError(error, stack); } diff --git a/data/lib/service/team/team_service.dart b/data/lib/service/team/team_service.dart index 1d119eaf..3741ead9 100644 --- a/data/lib/service/team/team_service.dart +++ b/data/lib/service/team/team_service.dart @@ -269,11 +269,10 @@ class TeamService { Future> getTeamsByIds(List teamIds) async { try { - final teamList = await _teamsCollection + return await _teamsCollection .where(FieldPath.documentId, whereIn: teamIds) .get() .then((value) => value.docs.map((e) => e.data()).toList()); - return teamList; } catch (error, stack) { throw AppError.fromError(error, stack); } diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index 55bdfab9..f45076cc 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -10,28 +10,28 @@ import '../user/user_service.dart'; final tournamentServiceProvider = Provider( (ref) => TournamentService( - fireStore: FirebaseFirestore.instance, - teamService: ref.read(teamServiceProvider), - matchService: ref.read(matchServiceProvider), - userService: ref.read(userServiceProvider), + FirebaseFirestore.instance, + ref.read(teamServiceProvider), + ref.read(matchServiceProvider), + ref.read(userServiceProvider), ), ); class TournamentService { - final FirebaseFirestore fireStore; - final TeamService teamService; - final MatchService matchService; - final UserService userService; - - TournamentService({ - required this.fireStore, - required this.teamService, - required this.matchService, - required this.userService, - }); + final FirebaseFirestore _firestore; + final TeamService _teamService; + final MatchService _matchService; + final UserService _userService; + + TournamentService( + this._firestore, + this._teamService, + this._matchService, + this._userService, + ); CollectionReference get _tournamentCollection => - fireStore.collection(FireStoreConst.tournamentCollection).withConverter( + _firestore.collection(FireStoreConst.tournamentCollection).withConverter( fromFirestore: TournamentModel.fromFireStore, toFirestore: (TournamentModel tournament, _) => tournament.toJson(), ); @@ -86,18 +86,18 @@ class TournamentService { final matchIds = tournament.match_ids; if (teamIds.isNotEmpty) { - final teams = await teamService.getTeamsByIds(teamIds); + final teams = await _teamService.getTeamsByIds(teamIds); tournament = tournament.copyWith(teams: teams); } if (matchIds.isNotEmpty) { - final matches = await matchService.getMatchesByIds(matchIds); + final matches = await _matchService.getMatchesByIds(matchIds); tournament = tournament.copyWith(matches: matches); } if (tournament.members.isNotEmpty) { final memberIds = tournament.members.map((e) => e.id).toList(); - final users = await userService.getUsersByIds(memberIds); + final users = await _userService.getUsersByIds(memberIds); final members = tournament.members.map((member) { final user = users.firstWhere((element) => element.id == member.id); diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 5fb4164b..ae1fb96f 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -167,7 +167,7 @@ "@_TOURNAMENT_DETAIL": { }, "tournament_detail_not_found_title": "No Tournament found", - "tournament_detail_not_found_description": "The tournament you are looking for does not available.", + "tournament_detail_not_found_description": "The tournament you are looking for is not available.", "tournament_detail_start_from_title": "Start from {date}", "@tournament_detail_start_from_title": { "description": "Start from {date}", diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart index 3969801b..d1d53714 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -42,7 +42,7 @@ class TournamentDetailStateViewNotifier }, onError: (e) { state = state.copyWith(error: e, loading: false); debugPrint( - "TournamentListViewNotifier: error while loading tournament list -> $e"); + "TournamentDetailStateViewNotifier: error while loading tournament list -> $e"); }); } From d22f175c7cd9ea373cd8529492ed1686512afd11 Mon Sep 17 00:00:00 2001 From: sidhdhi canopas <122426509+cp-sidhdhi-p@users.noreply.github.com> Date: Mon, 14 Oct 2024 09:12:27 +0530 Subject: [PATCH 20/33] Improve home screen content (#118) * show matches based on updated_at field * remove unused package * fix error prone implementation for date * revert unwanted change --- data/lib/api/match/match_model.dart | 1 + data/lib/api/match/match_model.freezed.dart | 39 ++++++-- data/lib/api/match/match_model.g.dart | 4 + data/lib/service/match/match_service.dart | 96 ++++++++++++++++++- .../utils/constant/firestore_constant.dart | 4 +- khelo/lib/ui/flow/home/home_view_model.dart | 42 +++----- .../add_match/add_match_view_model.dart | 1 + style/lib/pickers/date_and_time_picker.dart | 6 +- 8 files changed, 150 insertions(+), 43 deletions(-) diff --git a/data/lib/api/match/match_model.dart b/data/lib/api/match/match_model.dart index 66e2dd25..6832450b 100644 --- a/data/lib/api/match/match_model.dart +++ b/data/lib/api/match/match_model.dart @@ -50,6 +50,7 @@ class MatchModel with _$MatchModel { String? toss_winner_id, String? current_playing_team_id, RevisedTarget? revised_target, + @TimeStampJsonConverter() DateTime? updated_at, }) = _MatchModel; factory MatchModel.fromJson(Map json) => diff --git a/data/lib/api/match/match_model.freezed.dart b/data/lib/api/match/match_model.freezed.dart index 2050de3e..963d511e 100644 --- a/data/lib/api/match/match_model.freezed.dart +++ b/data/lib/api/match/match_model.freezed.dart @@ -56,6 +56,8 @@ mixin _$MatchModel { String? get toss_winner_id => throw _privateConstructorUsedError; String? get current_playing_team_id => throw _privateConstructorUsedError; RevisedTarget? get revised_target => throw _privateConstructorUsedError; + @TimeStampJsonConverter() + DateTime? get updated_at => throw _privateConstructorUsedError; /// Serializes this MatchModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -107,7 +109,8 @@ abstract class $MatchModelCopyWith<$Res> { TossDecision? toss_decision, String? toss_winner_id, String? current_playing_team_id, - RevisedTarget? revised_target}); + RevisedTarget? revised_target, + @TimeStampJsonConverter() DateTime? updated_at}); $UserModelCopyWith<$Res>? get referee; $RevisedTargetCopyWith<$Res>? get revised_target; @@ -159,6 +162,7 @@ class _$MatchModelCopyWithImpl<$Res, $Val extends MatchModel> Object? toss_winner_id = freezed, Object? current_playing_team_id = freezed, Object? revised_target = freezed, + Object? updated_at = freezed, }) { return _then(_value.copyWith( id: null == id @@ -285,6 +289,10 @@ class _$MatchModelCopyWithImpl<$Res, $Val extends MatchModel> ? _value.revised_target : revised_target // ignore: cast_nullable_to_non_nullable as RevisedTarget?, + updated_at: freezed == updated_at + ? _value.updated_at + : updated_at // ignore: cast_nullable_to_non_nullable + as DateTime?, ) as $Val); } @@ -359,7 +367,8 @@ abstract class _$$MatchModelImplCopyWith<$Res> TossDecision? toss_decision, String? toss_winner_id, String? current_playing_team_id, - RevisedTarget? revised_target}); + RevisedTarget? revised_target, + @TimeStampJsonConverter() DateTime? updated_at}); @override $UserModelCopyWith<$Res>? get referee; @@ -411,6 +420,7 @@ class __$$MatchModelImplCopyWithImpl<$Res> Object? toss_winner_id = freezed, Object? current_playing_team_id = freezed, Object? revised_target = freezed, + Object? updated_at = freezed, }) { return _then(_$MatchModelImpl( id: null == id @@ -537,6 +547,10 @@ class __$$MatchModelImplCopyWithImpl<$Res> ? _value.revised_target : revised_target // ignore: cast_nullable_to_non_nullable as RevisedTarget?, + updated_at: freezed == updated_at + ? _value.updated_at + : updated_at // ignore: cast_nullable_to_non_nullable + as DateTime?, )); } } @@ -579,7 +593,8 @@ class _$MatchModelImpl implements _MatchModel { this.toss_decision, this.toss_winner_id, this.current_playing_team_id, - this.revised_target}) + this.revised_target, + @TimeStampJsonConverter() this.updated_at}) : _teams = teams, _players = players, _team_ids = team_ids, @@ -764,10 +779,13 @@ class _$MatchModelImpl implements _MatchModel { final String? current_playing_team_id; @override final RevisedTarget? revised_target; + @override + @TimeStampJsonConverter() + final DateTime? updated_at; @override String toString() { - return 'MatchModel(id: $id, teams: $teams, match_type: $match_type, number_of_over: $number_of_over, over_per_bowler: $over_per_bowler, players: $players, team_ids: $team_ids, team_creator_ids: $team_creator_ids, power_play_overs1: $power_play_overs1, power_play_overs2: $power_play_overs2, power_play_overs3: $power_play_overs3, city: $city, ground: $ground, start_time: $start_time, start_at: $start_at, ball_type: $ball_type, pitch_type: $pitch_type, created_by: $created_by, umpires: $umpires, scorers: $scorers, commentators: $commentators, referee: $referee, umpire_ids: $umpire_ids, scorer_ids: $scorer_ids, commentator_ids: $commentator_ids, referee_id: $referee_id, match_status: $match_status, toss_decision: $toss_decision, toss_winner_id: $toss_winner_id, current_playing_team_id: $current_playing_team_id, revised_target: $revised_target)'; + return 'MatchModel(id: $id, teams: $teams, match_type: $match_type, number_of_over: $number_of_over, over_per_bowler: $over_per_bowler, players: $players, team_ids: $team_ids, team_creator_ids: $team_creator_ids, power_play_overs1: $power_play_overs1, power_play_overs2: $power_play_overs2, power_play_overs3: $power_play_overs3, city: $city, ground: $ground, start_time: $start_time, start_at: $start_at, ball_type: $ball_type, pitch_type: $pitch_type, created_by: $created_by, umpires: $umpires, scorers: $scorers, commentators: $commentators, referee: $referee, umpire_ids: $umpire_ids, scorer_ids: $scorer_ids, commentator_ids: $commentator_ids, referee_id: $referee_id, match_status: $match_status, toss_decision: $toss_decision, toss_winner_id: $toss_winner_id, current_playing_team_id: $current_playing_team_id, revised_target: $revised_target, updated_at: $updated_at)'; } @override @@ -828,7 +846,9 @@ class _$MatchModelImpl implements _MatchModel { other.current_playing_team_id, current_playing_team_id) || other.current_playing_team_id == current_playing_team_id) && (identical(other.revised_target, revised_target) || - other.revised_target == revised_target)); + other.revised_target == revised_target) && + (identical(other.updated_at, updated_at) || + other.updated_at == updated_at)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -865,7 +885,8 @@ class _$MatchModelImpl implements _MatchModel { toss_decision, toss_winner_id, current_playing_team_id, - revised_target + revised_target, + updated_at ]); /// Create a copy of MatchModel @@ -920,7 +941,8 @@ abstract class _MatchModel implements MatchModel { final TossDecision? toss_decision, final String? toss_winner_id, final String? current_playing_team_id, - final RevisedTarget? revised_target}) = _$MatchModelImpl; + final RevisedTarget? revised_target, + @TimeStampJsonConverter() final DateTime? updated_at}) = _$MatchModelImpl; factory _MatchModel.fromJson(Map json) = _$MatchModelImpl.fromJson; @@ -992,6 +1014,9 @@ abstract class _MatchModel implements MatchModel { String? get current_playing_team_id; @override RevisedTarget? get revised_target; + @override + @TimeStampJsonConverter() + DateTime? get updated_at; /// Create a copy of MatchModel /// with the given fields replaced by the non-null parameter values. diff --git a/data/lib/api/match/match_model.g.dart b/data/lib/api/match/match_model.g.dart index 0e8731de..3e141a21 100644 --- a/data/lib/api/match/match_model.g.dart +++ b/data/lib/api/match/match_model.g.dart @@ -68,6 +68,8 @@ _$MatchModelImpl _$$MatchModelImplFromJson(Map json) => _$MatchModelImpl( ? null : RevisedTarget.fromJson( Map.from(json['revised_target'] as Map)), + updated_at: _$JsonConverterFromJson( + json['updated_at'], const TimeStampJsonConverter().fromJson), ); Map _$$MatchModelImplToJson(_$MatchModelImpl instance) => @@ -100,6 +102,8 @@ Map _$$MatchModelImplToJson(_$MatchModelImpl instance) => 'toss_winner_id': instance.toss_winner_id, 'current_playing_team_id': instance.current_playing_team_id, 'revised_target': instance.revised_target?.toJson(), + 'updated_at': _$JsonConverterToJson( + instance.updated_at, const TimeStampJsonConverter().toJson), }; const _$MatchTypeEnumMap = { diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index c559cc62..73d46df2 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -70,6 +70,7 @@ class MatchService { pitch_type: PitchType.turf, created_by: '', match_status: MatchStatus.running, + updated_at: DateTime.now(), ); } @@ -163,8 +164,81 @@ class MatchService { }).handleError((error, stack) => throw AppError.fromError(error, stack)); } - Stream> streamMatches() { - return _matchCollection.snapshots().asyncMap((snapshot) async { + Stream> streamActiveRunningMatches() { + final DateTime now = DateTime.now(); + final DateTime oneAndHalfHoursAgo = + now.subtract(Duration(hours: 1, minutes: 30)); + + final Timestamp timestamp = Timestamp.fromDate(oneAndHalfHoursAgo); + + final filter = Filter.and( + Filter(FireStoreConst.matchStatus, isEqualTo: MatchStatus.running.value), + Filter(FireStoreConst.updatedAt, isGreaterThan: timestamp), + ); + return _matchCollection + .where(filter) + .snapshots() + .asyncMap((snapshot) async { + return await Future.wait( + snapshot.docs.map((mainDoc) async { + final match = mainDoc.data(); + + final List teams = await getTeamsList(match.teams); + return match.copyWith(teams: teams); + }).toList(), + ); + }).handleError((error, stack) => throw AppError.fromError(error, stack)); + } + + Stream> streamUpcomingMatches() { + final DateTime now = DateTime.now(); + final startOfDay = DateTime(now.year, now.month, now.day); + final DateTime aMonthAfter = DateTime(now.year, now.month + 1, now.day); + + final Timestamp timestampAfterMonth = Timestamp.fromDate(aMonthAfter); + final Timestamp timestampNow = Timestamp.fromDate(startOfDay); + + final filter = Filter.and( + Filter( + FireStoreConst.matchStatus, + isEqualTo: MatchStatus.yetToStart.value, + ), + Filter(FireStoreConst.startAt, isGreaterThanOrEqualTo: timestampNow), + Filter( + FireStoreConst.startAt, + isLessThanOrEqualTo: timestampAfterMonth, + ), + ); + return _matchCollection + .where(filter) + .snapshots() + .asyncMap((snapshot) async { + return await Future.wait( + snapshot.docs.map((mainDoc) async { + final match = mainDoc.data(); + + final List teams = await getTeamsList(match.teams); + return match.copyWith(teams: teams); + }).toList(), + ); + }).handleError((error, stack) => throw AppError.fromError(error, stack)); + } + + Stream> streamFinishedMatches() { + final DateTime now = DateTime.now(); + final DateTime fifteenDaysAgo = + DateTime(now.year, now.month, now.day).subtract(Duration(days: 15)); + + final Timestamp timestamp = Timestamp.fromDate(fifteenDaysAgo); + + final filter = Filter.and( + Filter(FireStoreConst.matchStatus, isEqualTo: MatchStatus.finish.value), + Filter(FireStoreConst.updatedAt, isGreaterThan: timestamp), + ); + return _matchCollection + .where(filter) + .snapshots() + .asyncMap((snapshot) async { return await Future.wait( snapshot.docs.map((mainDoc) async { final match = mainDoc.data(); @@ -272,6 +346,7 @@ class MatchService { FireStoreConst.tossWinnerId: tossWinnerId, FireStoreConst.tossDecision: tossDecision.value, FireStoreConst.currentPlayingTeamId: currentPlayingTeam, + FireStoreConst.updatedAt: DateTime.now(), }; await matchRef.update(tossDetails); @@ -286,6 +361,7 @@ class MatchService { final Map matchStatus = { FireStoreConst.matchStatus: status.value, + FireStoreConst.updatedAt: DateTime.now(), }; await matchRef.update(matchStatus); @@ -347,6 +423,7 @@ class MatchService { transaction.update(matchRef, { FireStoreConst.teams: [battingTeam.toJson(), bowlingTeam.toJson()], + FireStoreConst.updatedAt: DateTime.now(), }); } catch (error, stack) { throw AppError.fromError(error, stack); @@ -360,7 +437,10 @@ class MatchService { try { final matchRef = _matchCollection.doc(matchId); - await matchRef.update({FireStoreConst.currentPlayingTeamId: teamId}); + await matchRef.update({ + FireStoreConst.currentPlayingTeamId: teamId, + FireStoreConst.updatedAt: DateTime.now(), + }); } catch (error, stack) { throw AppError.fromError(error, stack); } @@ -404,7 +484,10 @@ class MatchService { transaction.update( matchRef, - {FireStoreConst.teams: updatedTeams.map((e) => e.toJson())}, + { + FireStoreConst.teams: updatedTeams.map((e) => e.toJson()).toList(), + FireStoreConst.updatedAt: DateTime.now(), + }, ); }); } catch (error, stack) { @@ -417,7 +500,10 @@ class MatchService { required RevisedTarget revisedTarget, }) async { await _matchCollection.doc(matchId).update( - {FireStoreConst.revisedTarget: revisedTarget.toJson()}, + { + FireStoreConst.revisedTarget: revisedTarget.toJson(), + FireStoreConst.updatedAt: DateTime.now(), + }, ); } diff --git a/data/lib/utils/constant/firestore_constant.dart b/data/lib/utils/constant/firestore_constant.dart index 278e4c96..4014a894 100644 --- a/data/lib/utils/constant/firestore_constant.dart +++ b/data/lib/utils/constant/firestore_constant.dart @@ -26,6 +26,8 @@ class FireStoreConst { static const String teamIds = "team_ids"; static const String teamCreatorIds = "team_creator_ids"; static const String revisedTarget = "revised_target"; + static const String updatedAt = "updated_at"; + static const String startAt = "start_at"; // innings field const static const String matchId = "match_id"; @@ -56,7 +58,7 @@ class FireStoreConst { // tournament field const static const String members = "members"; - } +} class DataConfig { static late DataConfig _instance; diff --git a/khelo/lib/ui/flow/home/home_view_model.dart b/khelo/lib/ui/flow/home/home_view_model.dart index cf26ea0f..72be2d20 100644 --- a/khelo/lib/ui/flow/home/home_view_model.dart +++ b/khelo/lib/ui/flow/home/home_view_model.dart @@ -1,9 +1,9 @@ import 'dart:async'; -import 'package:collection/collection.dart'; import 'package:data/api/match/match_model.dart'; import 'package:data/service/match/match_service.dart'; import 'package:data/storage/app_preferences.dart'; +import 'package:data/utils/combine_latest.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; @@ -39,12 +39,20 @@ class HomeViewNotifier extends StateNotifier { _streamSubscription?.cancel(); state = state.copyWith(loading: state.matches.isEmpty); - _streamSubscription = _matchService.streamMatches().listen( - (matches) { - final groupMatches = _groupMatches(matches); + final matchCombiner = combineLatest3( + _matchService.streamActiveRunningMatches(), + _matchService.streamUpcomingMatches(), + _matchService.streamFinishedMatches()); + + _streamSubscription = matchCombiner.listen( + (allMatches) { state = state.copyWith( - matches: matches, - groupMatches: groupMatches, + matches: [...allMatches.$1, ...allMatches.$2, ...allMatches.$3], + groupMatches: { + MatchStatusLabel.live: allMatches.$1, + MatchStatusLabel.upcoming: allMatches.$2, + MatchStatusLabel.finished: allMatches.$3, + }, loading: false, error: null, ); @@ -56,28 +64,6 @@ class HomeViewNotifier extends StateNotifier { ); } - Map> _groupMatches( - List matches) { - final groupedMatches = groupBy(matches, (match) { - switch (match.match_status) { - case MatchStatus.running: - return MatchStatusLabel.live; - case MatchStatus.yetToStart: - return MatchStatusLabel.upcoming; - case MatchStatus.abandoned: - case MatchStatus.finish: - return MatchStatusLabel.finished; - } - }); - return { - MatchStatusLabel.live: groupedMatches[MatchStatusLabel.live] ?? [], - MatchStatusLabel.upcoming: - groupedMatches[MatchStatusLabel.upcoming] ?? [], - MatchStatusLabel.finished: - groupedMatches[MatchStatusLabel.finished] ?? [], - }; - } - @override void dispose() { _streamSubscription?.cancel(); 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 00cebc51..d1609308 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 @@ -186,6 +186,7 @@ class AddMatchViewNotifier extends StateNotifier { ground: ground, start_time: state.matchTime, start_at: state.matchTime, + updated_at: DateTime.now(), created_by: _currentUserId ?? state.teamA?.created_by ?? "INVALID ID", ball_type: state.ballType, pitch_type: state.pitchType, diff --git a/style/lib/pickers/date_and_time_picker.dart b/style/lib/pickers/date_and_time_picker.dart index 4357d614..cdadf2e9 100644 --- a/style/lib/pickers/date_and_time_picker.dart +++ b/style/lib/pickers/date_and_time_picker.dart @@ -9,12 +9,14 @@ Future selectDate( required DateTime initialDate, required Function(DateTime) onDateSelected, }) async { + final now = DateTime.now(); + showDatePicker( context: context, helpText: helpText, initialDate: initialDate, - firstDate: DateTime.now(), - lastDate: DateTime(DateTime.now().year + 1), + firstDate: initialDate.isBefore(now) ? initialDate : now, + lastDate: DateTime(now.year + 1, now.month, now.day), builder: (context, child) { return Theme( data: context.brightness == Brightness.dark From ff7dfa503e825e405e33d599f82763ea13cfa732 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 16 Oct 2024 17:33:43 +0530 Subject: [PATCH 21/33] Implement tournament overview tab --- data/lib/api/tournament/tournament_model.dart | 16 +- .../tournament/tournament_model.freezed.dart | 232 +++++++++- .../api/tournament/tournament_model.g.dart | 14 + .../ball_score/ball_score_service.dart | 21 + data/lib/service/match/match_service.dart | 11 +- .../tournament/tournament_service.dart | 39 +- khelo/assets/locales/app_en.arb | 10 +- khelo/lib/components/won_by_message_text.dart | 52 ++- .../lib/domain/formatter/date_formatter.dart | 24 +- khelo/lib/ui/flow/home/home_screen.dart | 2 +- .../tabs/tournament_detail_overview_tab.dart | 436 ++++++++++++++++++ .../detail/tournament_detail_screen.dart | 9 +- .../detail/tournament_detail_view_model.dart | 1 + .../tournament/tournament_list_screen.dart | 14 +- style/lib/extensions/date_extensions.dart | 4 + 15 files changed, 842 insertions(+), 43 deletions(-) create mode 100644 khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index 568291b2..f7bcb841 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -34,6 +34,9 @@ class TournamentModel with _$TournamentModel { @JsonKey(includeFromJson: false, includeToJson: false) @Default([]) List matches, + @JsonKey(includeFromJson: false, includeToJson: false) + @Default([]) + List keyStats, }) = _TournamentModel; factory TournamentModel.fromJson(Map json) => @@ -52,7 +55,8 @@ class TournamentMember with _$TournamentMember { const factory TournamentMember({ required String id, @JsonKey(includeToJson: false, includeFromJson: false) - @Default(UserModel(id: '')) UserModel user, + @Default(UserModel(id: '')) + UserModel user, @Default(TournamentMemberRole.admin) TournamentMemberRole role, }) = _TournamentMember; @@ -85,3 +89,13 @@ enum TournamentMemberRole { bool get isAdmin => this == TournamentMemberRole.admin; } + +@freezed +class PlayerKeyStat with _$PlayerKeyStat { + @JsonSerializable(explicitToJson: true) + const factory PlayerKeyStat({ + required String teamName, + required UserModel player, + required int runs, + }) = _PlayerKeyStat; +} diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index ed37d162..2e3bc29e 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -39,6 +39,8 @@ mixin _$TournamentModel { List get teams => throw _privateConstructorUsedError; @JsonKey(includeFromJson: false, includeToJson: false) List get matches => throw _privateConstructorUsedError; + @JsonKey(includeFromJson: false, includeToJson: false) + List get keyStats => throw _privateConstructorUsedError; /// Serializes this TournamentModel to a JSON map. Map toJson() => throw _privateConstructorUsedError; @@ -72,7 +74,9 @@ abstract class $TournamentModelCopyWith<$Res> { @JsonKey(includeFromJson: false, includeToJson: false) List teams, @JsonKey(includeFromJson: false, includeToJson: false) - List matches}); + List matches, + @JsonKey(includeFromJson: false, includeToJson: false) + List keyStats}); } /// @nodoc @@ -104,6 +108,7 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> Object? match_ids = null, Object? teams = null, Object? matches = null, + Object? keyStats = null, }) { return _then(_value.copyWith( id: null == id @@ -162,6 +167,10 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> ? _value.matches : matches // ignore: cast_nullable_to_non_nullable as List, + keyStats: null == keyStats + ? _value.keyStats + : keyStats // ignore: cast_nullable_to_non_nullable + as List, ) as $Val); } } @@ -190,7 +199,9 @@ abstract class _$$TournamentModelImplCopyWith<$Res> @JsonKey(includeFromJson: false, includeToJson: false) List teams, @JsonKey(includeFromJson: false, includeToJson: false) - List matches}); + List matches, + @JsonKey(includeFromJson: false, includeToJson: false) + List keyStats}); } /// @nodoc @@ -220,6 +231,7 @@ class __$$TournamentModelImplCopyWithImpl<$Res> Object? match_ids = null, Object? teams = null, Object? matches = null, + Object? keyStats = null, }) { return _then(_$TournamentModelImpl( id: null == id @@ -278,6 +290,10 @@ class __$$TournamentModelImplCopyWithImpl<$Res> ? _value._matches : matches // ignore: cast_nullable_to_non_nullable as List, + keyStats: null == keyStats + ? _value._keyStats + : keyStats // ignore: cast_nullable_to_non_nullable + as List, )); } } @@ -302,12 +318,15 @@ class _$TournamentModelImpl implements _TournamentModel { @JsonKey(includeFromJson: false, includeToJson: false) final List teams = const [], @JsonKey(includeFromJson: false, includeToJson: false) - final List matches = const []}) + final List matches = const [], + @JsonKey(includeFromJson: false, includeToJson: false) + final List keyStats = const []}) : _members = members, _team_ids = team_ids, _match_ids = match_ids, _teams = teams, - _matches = matches; + _matches = matches, + _keyStats = keyStats; factory _$TournamentModelImpl.fromJson(Map json) => _$$TournamentModelImplFromJson(json); @@ -378,9 +397,18 @@ class _$TournamentModelImpl implements _TournamentModel { return EqualUnmodifiableListView(_matches); } + final List _keyStats; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get keyStats { + if (_keyStats is EqualUnmodifiableListView) return _keyStats; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_keyStats); + } + @override String toString() { - return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, banner_img_url: $banner_img_url, type: $type, members: $members, created_by: $created_by, created_at: $created_at, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches)'; + return 'TournamentModel(id: $id, name: $name, profile_img_url: $profile_img_url, banner_img_url: $banner_img_url, type: $type, members: $members, created_by: $created_by, created_at: $created_at, start_date: $start_date, end_date: $end_date, team_ids: $team_ids, match_ids: $match_ids, teams: $teams, matches: $matches, keyStats: $keyStats)'; } @override @@ -408,7 +436,8 @@ class _$TournamentModelImpl implements _TournamentModel { const DeepCollectionEquality() .equals(other._match_ids, _match_ids) && const DeepCollectionEquality().equals(other._teams, _teams) && - const DeepCollectionEquality().equals(other._matches, _matches)); + const DeepCollectionEquality().equals(other._matches, _matches) && + const DeepCollectionEquality().equals(other._keyStats, _keyStats)); } @JsonKey(includeFromJson: false, includeToJson: false) @@ -428,7 +457,8 @@ class _$TournamentModelImpl implements _TournamentModel { const DeepCollectionEquality().hash(_team_ids), const DeepCollectionEquality().hash(_match_ids), const DeepCollectionEquality().hash(_teams), - const DeepCollectionEquality().hash(_matches)); + const DeepCollectionEquality().hash(_matches), + const DeepCollectionEquality().hash(_keyStats)); /// Create a copy of TournamentModel /// with the given fields replaced by the non-null parameter values. @@ -464,7 +494,9 @@ abstract class _TournamentModel implements TournamentModel { @JsonKey(includeFromJson: false, includeToJson: false) final List teams, @JsonKey(includeFromJson: false, includeToJson: false) - final List matches}) = _$TournamentModelImpl; + final List matches, + @JsonKey(includeFromJson: false, includeToJson: false) + final List keyStats}) = _$TournamentModelImpl; factory _TournamentModel.fromJson(Map json) = _$TournamentModelImpl.fromJson; @@ -502,6 +534,9 @@ abstract class _TournamentModel implements TournamentModel { @override @JsonKey(includeFromJson: false, includeToJson: false) List get matches; + @override + @JsonKey(includeFromJson: false, includeToJson: false) + List get keyStats; /// Create a copy of TournamentModel /// with the given fields replaced by the non-null parameter values. @@ -726,3 +761,184 @@ abstract class _TournamentMember implements TournamentMember { _$$TournamentMemberImplCopyWith<_$TournamentMemberImpl> get copyWith => throw _privateConstructorUsedError; } + +/// @nodoc +mixin _$PlayerKeyStat { + String get teamName => throw _privateConstructorUsedError; + UserModel get player => throw _privateConstructorUsedError; + int get runs => throw _privateConstructorUsedError; + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $PlayerKeyStatCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $PlayerKeyStatCopyWith<$Res> { + factory $PlayerKeyStatCopyWith( + PlayerKeyStat value, $Res Function(PlayerKeyStat) then) = + _$PlayerKeyStatCopyWithImpl<$Res, PlayerKeyStat>; + @useResult + $Res call({String teamName, UserModel player, int runs}); + + $UserModelCopyWith<$Res> get player; +} + +/// @nodoc +class _$PlayerKeyStatCopyWithImpl<$Res, $Val extends PlayerKeyStat> + implements $PlayerKeyStatCopyWith<$Res> { + _$PlayerKeyStatCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? teamName = null, + Object? player = null, + Object? runs = null, + }) { + return _then(_value.copyWith( + teamName: null == teamName + ? _value.teamName + : teamName // ignore: cast_nullable_to_non_nullable + as String, + player: null == player + ? _value.player + : player // ignore: cast_nullable_to_non_nullable + as UserModel, + runs: null == runs + ? _value.runs + : runs // ignore: cast_nullable_to_non_nullable + as int, + ) as $Val); + } + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserModelCopyWith<$Res> get player { + return $UserModelCopyWith<$Res>(_value.player, (value) { + return _then(_value.copyWith(player: value) as $Val); + }); + } +} + +/// @nodoc +abstract class _$$PlayerKeyStatImplCopyWith<$Res> + implements $PlayerKeyStatCopyWith<$Res> { + factory _$$PlayerKeyStatImplCopyWith( + _$PlayerKeyStatImpl value, $Res Function(_$PlayerKeyStatImpl) then) = + __$$PlayerKeyStatImplCopyWithImpl<$Res>; + @override + @useResult + $Res call({String teamName, UserModel player, int runs}); + + @override + $UserModelCopyWith<$Res> get player; +} + +/// @nodoc +class __$$PlayerKeyStatImplCopyWithImpl<$Res> + extends _$PlayerKeyStatCopyWithImpl<$Res, _$PlayerKeyStatImpl> + implements _$$PlayerKeyStatImplCopyWith<$Res> { + __$$PlayerKeyStatImplCopyWithImpl( + _$PlayerKeyStatImpl _value, $Res Function(_$PlayerKeyStatImpl) _then) + : super(_value, _then); + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? teamName = null, + Object? player = null, + Object? runs = null, + }) { + return _then(_$PlayerKeyStatImpl( + teamName: null == teamName + ? _value.teamName + : teamName // ignore: cast_nullable_to_non_nullable + as String, + player: null == player + ? _value.player + : player // ignore: cast_nullable_to_non_nullable + as UserModel, + runs: null == runs + ? _value.runs + : runs // ignore: cast_nullable_to_non_nullable + as int, + )); + } +} + +/// @nodoc + +@JsonSerializable(explicitToJson: true) +class _$PlayerKeyStatImpl implements _PlayerKeyStat { + const _$PlayerKeyStatImpl( + {required this.teamName, required this.player, required this.runs}); + + @override + final String teamName; + @override + final UserModel player; + @override + final int runs; + + @override + String toString() { + return 'PlayerKeyStat(teamName: $teamName, player: $player, runs: $runs)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$PlayerKeyStatImpl && + (identical(other.teamName, teamName) || + other.teamName == teamName) && + (identical(other.player, player) || other.player == player) && + (identical(other.runs, runs) || other.runs == runs)); + } + + @override + int get hashCode => Object.hash(runtimeType, teamName, player, runs); + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$PlayerKeyStatImplCopyWith<_$PlayerKeyStatImpl> get copyWith => + __$$PlayerKeyStatImplCopyWithImpl<_$PlayerKeyStatImpl>(this, _$identity); +} + +abstract class _PlayerKeyStat implements PlayerKeyStat { + const factory _PlayerKeyStat( + {required final String teamName, + required final UserModel player, + required final int runs}) = _$PlayerKeyStatImpl; + + @override + String get teamName; + @override + UserModel get player; + @override + int get runs; + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$PlayerKeyStatImplCopyWith<_$PlayerKeyStatImpl> get copyWith => + throw _privateConstructorUsedError; +} diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index 475a8cd0..de9022c5 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -97,3 +97,17 @@ const _$TournamentMemberRoleEnumMap = { TournamentMemberRole.organizer: 'organizer', TournamentMemberRole.admin: 'admin', }; + +_$PlayerKeyStatImpl _$$PlayerKeyStatImplFromJson(Map json) => + _$PlayerKeyStatImpl( + teamName: json['teamName'] as String, + player: UserModel.fromJson(json['player'] as Map), + runs: (json['runs'] as num).toInt(), + ); + +Map _$$PlayerKeyStatImplToJson(_$PlayerKeyStatImpl instance) => + { + 'teamName': instance.teamName, + 'player': instance.player.toJson(), + 'runs': instance.runs, + }; diff --git a/data/lib/service/ball_score/ball_score_service.dart b/data/lib/service/ball_score/ball_score_service.dart index 144920ea..3a754e90 100644 --- a/data/lib/service/ball_score/ball_score_service.dart +++ b/data/lib/service/ball_score/ball_score_service.dart @@ -182,6 +182,27 @@ class BallScoreService { throw AppError.fromError(error, stack); } } + + Future getPlayerTotalRuns(String matchId, String playerId) async { + try { + final filter = Filter.and( + Filter(FireStoreConst.matchId, isEqualTo: matchId), + Filter(FireStoreConst.batsmanId, isEqualTo: playerId), + ); + + final snapshot = await _ballScoreCollection.where(filter).get(); + final totalRuns = snapshot.docs.fold( + 0, + (total, doc) { + final runsScored = doc.data().runs_scored; + return runsScored > 0 ? total + runsScored : total; + }, + ); + return totalRuns; + } catch (error, stack) { + throw AppError.fromError(error, stack); + } + } } class BallScoreChange { diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index 1a0b0a9b..deda9c7a 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -577,10 +577,13 @@ class MatchService { Future> getMatchesByIds(List matchIds) async { try { - return await _matchCollection - .where(FieldPath.documentId, whereIn: matchIds) - .get() - .then((value) => value.docs.map((e) => e.data()).toList()); + final List matches = []; + for (var matchId in matchIds) { + final match = await getMatchById(matchId); + matches.add(match); + } + + return matches; } catch (error, stack) { throw AppError.fromError(error, stack); } diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index f45076cc..c252a5dc 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -1,9 +1,11 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../api/match/match_model.dart'; import '../../api/tournament/tournament_model.dart'; import '../../errors/app_error.dart'; import '../../utils/constant/firestore_constant.dart'; +import '../ball_score/ball_score_service.dart'; import '../match/match_service.dart'; import '../team/team_service.dart'; import '../user/user_service.dart'; @@ -14,6 +16,7 @@ final tournamentServiceProvider = Provider( ref.read(teamServiceProvider), ref.read(matchServiceProvider), ref.read(userServiceProvider), + ref.read(ballScoreServiceProvider), ), ); @@ -22,12 +25,14 @@ class TournamentService { final TeamService _teamService; final MatchService _matchService; final UserService _userService; + final BallScoreService _ballScoreService; TournamentService( this._firestore, this._teamService, this._matchService, this._userService, + this._ballScoreService, ); CollectionReference get _tournamentCollection => @@ -92,7 +97,9 @@ class TournamentService { if (matchIds.isNotEmpty) { final matches = await _matchService.getMatchesByIds(matchIds); - tournament = tournament.copyWith(matches: matches); + final keyStats = await getKeyStats(matches); + tournament = + tournament.copyWith(matches: matches, keyStats: keyStats); } if (tournament.members.isNotEmpty) { @@ -111,4 +118,34 @@ class TournamentService { } }).handleError((error, stack) => throw AppError.fromError(error, stack)); } + + Future> getKeyStats(List matches) async { + final Map playerStatsMap = {}; + + for (var match in matches) { + for (var team in match.teams) { + for (var player in team.squad) { + final totalRuns = + await _ballScoreService.getPlayerTotalRuns(match.id, player.id); + + if (totalRuns > 0) { + if (playerStatsMap.containsKey(player.id)) { + playerStatsMap[player.id] = playerStatsMap[player.id]!.copyWith( + runs: playerStatsMap[player.id]!.runs + totalRuns, + ); + } else { + playerStatsMap[player.id] = PlayerKeyStat( + teamName: team.team.name, + player: player.player, + runs: totalRuns, + ); + } + } + } + } + } + + return playerStatsMap.values.toList() + ..sort((a, b) => b.runs.compareTo(a.runs)); + } } diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2c11657b..2811d3ca 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -59,6 +59,7 @@ "common_second_inning_title": "Second inning", "common_qr_code_title": "QR Code", "common_verify_title": "Verify", + "common_view_all": "View all", "common_obscure_phone_number_text": "{countryCode} ***** ***{lastDigits}", "@common_obscure_phone_number_text": { "description": "+{countryCode} ***** ***{lastDigits}", @@ -118,7 +119,6 @@ "home_screen_create_match_btn": "Create match", "home_screen_set_up_team_title": "Set up a team in minutes.", "home_screen_create_team_btn": "Create team", - "home_screen_view_all_btn": "View all", "home_screen_no_matches_title": "No Matches in this area!", "home_screen_no_matches_description_text": "Enjoy the freedom of creating your own cricket matches or teams.", @@ -183,6 +183,14 @@ "tournament_detail_points_table_tab": "Points Table", "tournament_detail_stats_tab": "Stats", + "tournament_detail_overview_info_title": "Tournament info", + "tournament_detail_overview_type_title": "Type", + "tournament_detail_overview_duration_title": "Duration", + "tournament_detail_overview_teams_squads_title": "Teams Squads", + "tournament_detail_overview_key_stats_title": "Key Stats", + "tournament_detail_overview_key_stats_most_runs_title": "Most Runs", + "tournament_detail_overview_featured_matches_title": "Featured Matches", + "@_TOURNAMENT_TYPE":{ }, "tournament_type_knock_out": "Knockout", diff --git a/khelo/lib/components/won_by_message_text.dart b/khelo/lib/components/won_by_message_text.dart index b65b1041..4b6c8586 100644 --- a/khelo/lib/components/won_by_message_text.dart +++ b/khelo/lib/components/won_by_message_text.dart @@ -2,17 +2,20 @@ import 'package:data/api/match/match_model.dart'; import 'package:flutter/cupertino.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/extensions/string_extensions.dart'; import 'package:style/extensions/context_extensions.dart'; import 'package:style/text/app_text_style.dart'; class WonByMessageText extends StatelessWidget { final MatchResult? matchResult; final TextStyle? textStyle; + final bool isTournament; const WonByMessageText({ super.key, this.matchResult, this.textStyle, + this.isTournament = false, }); @override @@ -26,21 +29,42 @@ class WonByMessageText extends StatelessWidget { AppTextStyle.subtitle1 .copyWith(color: context.colorScheme.textPrimary)); } - return Text.rich(TextSpan( - text: matchResult!.teamName, - style: textStyle ?? - AppTextStyle.subtitle2 - .copyWith(color: context.colorScheme.textPrimary), + + if (isTournament) { + return Column( children: [ - TextSpan( - text: context.l10n.score_board_won_by_title, - style: textStyle ?? - AppTextStyle.body2 - .copyWith(color: context.colorScheme.textDisabled)), - TextSpan( - text: matchResult!.winType - .getString(context, matchResult!.difference), + Text( + '${matchResult!.teamName.initials(limit: 2)} Won', + style: textStyle ?? + AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + Text( + 'by ${matchResult!.winType.getString(context, matchResult!.difference)}', + style: textStyle ?? + AppTextStyle.caption.copyWith( + color: context.colorScheme.textDisabled, + ), ), - ])); + ], + ); + } else { + return Text.rich(TextSpan( + text: matchResult!.teamName, + style: textStyle ?? + AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + children: [ + TextSpan( + text: context.l10n.score_board_won_by_title, + style: textStyle ?? + AppTextStyle.body2 + .copyWith(color: context.colorScheme.textDisabled)), + TextSpan( + text: matchResult!.winType + .getString(context, matchResult!.difference), + ), + ])); + } } } diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart index 478c3ff2..09019756 100644 --- a/khelo/lib/domain/formatter/date_formatter.dart +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:intl/intl.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:style/extensions/date_extensions.dart'; enum DateFormatType { dateAndTime, @@ -42,11 +43,30 @@ extension DateFormatter on DateTime { } } + String relativeTime(BuildContext context) { + final today = DateTime.now(); + final tomorrow = today.add(const Duration(days: 1)); + + String day; + if (isSameDay(today)) { + day = 'Today'; + } else if (isSameDay(tomorrow)) { + day = 'Tomorrow'; + } else if (year == today.year) { + day = DateFormat('d MMM').format(this); + } else { + day = DateFormat('d MMM yyyy').format(this); + } + + return day; + } + static String formatDateRange( BuildContext context, { required DateTime startDate, - required DateTime endDate, + DateTime? endDate, required DateFormatType formatType, }) => - "${startDate.format(context, formatType)} - ${endDate.format(context, formatType)}"; + endDate?.format(context, formatType) ?? + "${startDate.format(context, formatType)} - ${endDate!.format(context, formatType)}"; } diff --git a/khelo/lib/ui/flow/home/home_screen.dart b/khelo/lib/ui/flow/home/home_screen.dart index 1d5facc9..26fa28a6 100644 --- a/khelo/lib/ui/flow/home/home_screen.dart +++ b/khelo/lib/ui/flow/home/home_screen.dart @@ -193,7 +193,7 @@ class _HomeScreenState extends ConsumerState { Visibility( visible: isViewAllShow, child: Text( - context.l10n.home_screen_view_all_btn, + context.l10n.common_view_all, style: AppTextStyle.button.copyWith( color: context.colorScheme.primary, ), diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart new file mode 100644 index 00000000..0b9aa8c5 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -0,0 +1,436 @@ +import 'package:data/api/match/match_model.dart'; +import 'package:data/api/team/team_model.dart'; +import 'package:data/api/tournament/tournament_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:khelo/components/won_by_message_text.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/enum_extensions.dart'; +import 'package:khelo/domain/extensions/string_extensions.dart'; +import 'package:khelo/domain/formatter/date_formatter.dart'; +import 'package:khelo/ui/app_route.dart'; +import 'package:style/animations/on_tap_scale.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +import '../../../../../components/image_avatar.dart'; + +class TournamentDetailOverviewTab extends ConsumerStatefulWidget { + final TournamentModel tournament; + + const TournamentDetailOverviewTab({ + super.key, + required this.tournament, + }); + + @override + ConsumerState createState() => + _TournamentDetailOverviewTabState(); +} + +class _TournamentDetailOverviewTabState + extends ConsumerState { + @override + Widget build(BuildContext context) { + return Container( + color: context.colorScheme.containerLow, + child: ListView( + physics: NeverScrollableScrollPhysics(), + padding: context.mediaQueryPadding.copyWith(top: 0) + + EdgeInsets.symmetric(horizontal: 16), + children: [ + _featuredMatchesView(context, widget.tournament.matches), + _keyStatsView(context, widget.tournament.keyStats), + _teamsSquadsView(context, widget.tournament.teams), + _infoView(context, widget.tournament), + ], + ), + ); + } + + Widget _featuredMatchesView(BuildContext context, List matches) { + if (matches.isEmpty) return SizedBox(); + + return Padding( + padding: const EdgeInsets.only(top: 8), + child: Column( + children: [ + _header( + context, + title: + context.l10n.tournament_detail_overview_featured_matches_title, + showViewAll: false, + onViewAll: () {}, + ), + const SizedBox(height: 8), + ...matches.map((match) => _matchCellView(context, match, true)), + ], + ), + ); + } + + Widget _matchCellView( + BuildContext context, MatchModel match, bool isFinalMatch) { + return Container( + padding: isFinalMatch + ? EdgeInsets.only(right: 16) + : EdgeInsets.symmetric(horizontal: 16, vertical: 24), + margin: EdgeInsets.symmetric(vertical: 8), + decoration: BoxDecoration( + color: context.colorScheme.surface, + borderRadius: BorderRadius.circular(16), + ), + child: Row( + children: [ + if (isFinalMatch) ...[ + RotatedBox( + quarterTurns: 7, + child: _finalTag(), + ), + ], + _buildTeamInfo(team: match.teams.first.team), + const Spacer(), + Column( + children: [ + if (match.matchResult != null) ...[ + WonByMessageText( + isTournament: true, + matchResult: match.matchResult, + ), + ] else ...[ + Text( + match.start_at?.format(context, DateFormatType.time) ?? + DateTime.now().format(context, DateFormatType.time), + style: AppTextStyle.caption + .copyWith(color: context.colorScheme.textDisabled), + ), + Text( + match.start_at?.relativeTime(context) ?? + DateTime.now().relativeTime(context), + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + ], + ], + ), + const Spacer(), + _buildTeamInfo(team: match.teams.last.team, isSecond: true), + ], + ), + ); + } + + Widget _finalTag() { + return Container( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: context.colorScheme.primary.withOpacity(0.2), + borderRadius: BorderRadius.circular(30), + ), + child: Text( + "Semi Final 1", + style: TextStyle(fontFamily: AppTextStyle.poppinsFontFamily), + ), + ); + } + + Widget _buildTeamInfo({ + required TeamModel team, + bool isSecond = false, + }) { + final initials = team.name_initial ?? team.name.initials(limit: 2); + return Row( + children: [ + if (isSecond) ...[ + Text( + initials, + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, + ), + ), + const SizedBox(width: 16), + ImageAvatar( + initial: initials, + imageUrl: team.profile_img_url, + size: 40, + ), + ] else ...[ + ImageAvatar( + initial: initials, + imageUrl: team.profile_img_url, + size: 40, + ), + const SizedBox(width: 16), + Text( + initials, + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, + ), + ), + ], + ], + ); + } + + Widget _keyStatsView(BuildContext context, List keyStats) { + if (keyStats.isEmpty) return SizedBox(); + + return Padding( + padding: const EdgeInsets.only(top: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _header( + context, + showViewAll: keyStats.length > 4, + title: context.l10n.tournament_detail_overview_key_stats_title, + onViewAll: () {}, + ), + const SizedBox(height: 8), + GridView.builder( + shrinkWrap: true, + physics: NeverScrollableScrollPhysics(), + itemCount: keyStats.take(4).length, + padding: EdgeInsets.zero, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 16, + mainAxisSpacing: 16, + mainAxisExtent: 115, + ), + itemBuilder: (context, index) => + _keyStatsCellView(context, keyStats[index]), + ) + ], + ), + ); + } + + Widget _keyStatsCellView(BuildContext context, PlayerKeyStat keyStat) { + return Container( + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surface, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + context + .l10n.tournament_detail_overview_key_stats_most_runs_title, + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.positive), + ), + Text( + keyStat.runs.toString(), + style: AppTextStyle.subtitle1.copyWith( + color: context.colorScheme.textPrimary, + ), + ) + ], + ), + Divider(color: context.colorScheme.outline), + Row( + children: [ + ImageAvatar( + initial: keyStat.player.nameInitial, + imageUrl: keyStat.player.profile_img_url, + size: 40, + ), + const SizedBox(width: 8), + Flexible( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + keyStat.player.name ?? '', + style: AppTextStyle.subtitle2.copyWith( + color: context.colorScheme.textPrimary, + ), + overflow: TextOverflow.ellipsis, + ), + Text( + keyStat.teamName, + style: AppTextStyle.caption.copyWith( + color: context.colorScheme.textDisabled, + ), + ) + ], + ), + ) + ], + ), + ], + ), + ); + } + + Widget _teamsSquadsView(BuildContext context, List teams) { + if (teams.isEmpty) return SizedBox(); + + return Padding( + padding: const EdgeInsets.only(top: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _header( + context, + showViewAll: teams.length >= 3, + title: context.l10n.tournament_detail_overview_teams_squads_title, + onViewAll: () {}, + ), + const SizedBox(height: 8), + SizedBox( + height: 130, + child: ListView.separated( + shrinkWrap: true, + scrollDirection: Axis.horizontal, + itemBuilder: (context, index) => _teamCellView( + context, + teams[index], + ), + separatorBuilder: (context, index) => const SizedBox(width: 16), + itemCount: teams.length, + ), + ) + ], + ), + ); + } + + Widget _teamCellView(BuildContext context, TeamModel team) { + return OnTapScale( + onTap: () => AppRoute.teamDetail(teamId: team.id).push(context), + child: Container( + width: 120, + padding: EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.surface, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + children: [ + ImageAvatar( + initial: team.name_initial ?? team.name.initials(limit: 1), + imageUrl: team.profile_img_url, + size: 40, + ), + const SizedBox(height: 16), + Flexible( + child: Text( + team.name, + style: AppTextStyle.subtitle2.copyWith( + color: context.colorScheme.textPrimary, + ), + textAlign: TextAlign.center, + ), + ) + ], + ), + ), + ); + } + + Widget _infoView(BuildContext context, TournamentModel tournament) { + return Padding( + padding: const EdgeInsets.only(top: 24), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _header(context, + title: context.l10n.tournament_detail_overview_info_title), + const SizedBox(height: 8), + Container( + decoration: BoxDecoration( + color: context.colorScheme.surface, + borderRadius: BorderRadius.circular(16), + ), + child: Column( + children: [ + _infoCellView( + context, + context.l10n.tournament_detail_overview_info_title, + tournament.name, + ), + Divider(color: context.colorScheme.outline, height: 1), + _infoCellView( + context, + context.l10n.tournament_detail_overview_duration_title, + DateFormatter.formatDateRange( + context, + startDate: tournament.start_date, + endDate: tournament.end_date, + formatType: DateFormatType.dayMonth, + ), + ), + Divider(color: context.colorScheme.outline, height: 1), + _infoCellView( + context, + context.l10n.tournament_detail_overview_type_title, + tournament.type.getString(context), + ), + ], + ), + ), + ], + ), + ); + } + + Widget _infoCellView(BuildContext context, String title, String value) { + return Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Text( + title, + style: AppTextStyle.subtitle3 + .copyWith(color: context.colorScheme.textSecondary), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + value, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + textAlign: TextAlign.end, + ), + ), + ], + ), + ); + } + + Widget _header( + BuildContext context, { + required String title, + bool showViewAll = false, + Function()? onViewAll, + }) { + return Row( + children: [ + Text( + title, + style: AppTextStyle.header4 + .copyWith(color: context.colorScheme.textPrimary), + ), + const Spacer(), + Opacity( + opacity: showViewAll ? 1 : 0, + child: TextButton( + onPressed: onViewAll, + child: Text( + context.l10n.common_view_all, + style: AppTextStyle.subtitle3 + .copyWith(color: context.colorScheme.primary), + ), + ), + ) + ], + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index 1d999d02..5dca3c19 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -8,6 +8,7 @@ import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/flow/tournament/components/sliver_header_delegate.dart'; +import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart'; import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart'; import 'package:style/button/more_option_button.dart'; import 'package:style/button/tab_button.dart'; @@ -101,7 +102,7 @@ class _TournamentDetailScreenState ), SliverFillRemaining( child: _content(context, state), - ) + ), ], ); } @@ -110,8 +111,10 @@ class _TournamentDetailScreenState return PageView( controller: _controller, onPageChanged: notifier.onTabChange, - children: const [ - //Add Tab view + children: [ + TournamentDetailOverviewTab( + tournament: state.tournament!, + ), ], ); } diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart index d1d53714..e26eedb9 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -51,6 +51,7 @@ class TournamentDetailStateViewNotifier state = state.copyWith(selectedTab: tab); } } + @override void dispose() { _tournamentSubscription?.cancel(); diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index e8b6ed53..b9adfada 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -203,14 +203,12 @@ class _TournamentListScreenState extends ConsumerState TournamentModel tournament, ) { return Text.rich(TextSpan( - text: (tournament.end_date != null) - ? DateFormatter.formatDateRange( - context, - startDate: tournament.start_date, - endDate: tournament.end_date!, - formatType: DateFormatType.dayMonth, - ) - : tournament.start_date.format(context, DateFormatType.dayMonth), + text: DateFormatter.formatDateRange( + context, + startDate: tournament.start_date, + endDate: tournament.end_date!, + formatType: DateFormatType.dayMonth, + ), style: AppTextStyle.caption .copyWith(color: context.colorScheme.textDisabled), children: [ diff --git a/style/lib/extensions/date_extensions.dart b/style/lib/extensions/date_extensions.dart index f6f75f26..26334e22 100644 --- a/style/lib/extensions/date_extensions.dart +++ b/style/lib/extensions/date_extensions.dart @@ -1,5 +1,9 @@ +import 'package:flutter/material.dart'; + extension DateTimeExtensions on DateTime { DateTime get startOfDay => DateTime(year, month, day); + bool isSameDay(DateTime? date) => DateUtils.isSameDay(this, date); + DateTime get startOfMonth => DateTime(year, month, 1); } From 22a6544b00aef97c9189d776e328c91f7bce2cb0 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 18 Oct 2024 11:09:54 +0530 Subject: [PATCH 22/33] minor changes --- .../tabs/tournament_detail_overview_tab.dart | 42 +++++-------------- .../detail/tournament_detail_screen.dart | 2 +- 2 files changed, 11 insertions(+), 33 deletions(-) diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index 0b9aa8c5..898664aa 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -35,9 +35,8 @@ class _TournamentDetailOverviewTabState return Container( color: context.colorScheme.containerLow, child: ListView( - physics: NeverScrollableScrollPhysics(), - padding: context.mediaQueryPadding.copyWith(top: 0) + - EdgeInsets.symmetric(horizontal: 16), + padding: EdgeInsets.all(16) + .copyWith(bottom: context.mediaQueryPadding.bottom + 40), children: [ _featuredMatchesView(context, widget.tournament.matches), _keyStatsView(context, widget.tournament.keyStats), @@ -52,29 +51,28 @@ class _TournamentDetailOverviewTabState if (matches.isEmpty) return SizedBox(); return Padding( - padding: const EdgeInsets.only(top: 8), + padding: const EdgeInsets.only(top: 16), child: Column( children: [ _header( context, title: context.l10n.tournament_detail_overview_featured_matches_title, - showViewAll: false, + showViewAll: matches.length > 3, onViewAll: () {}, ), - const SizedBox(height: 8), - ...matches.map((match) => _matchCellView(context, match, true)), + ...List.generate( + matches.take(3).length, + (index) => _matchCellView(context, matches[index]), + ) ], ), ); } - Widget _matchCellView( - BuildContext context, MatchModel match, bool isFinalMatch) { + Widget _matchCellView(BuildContext context, MatchModel match) { return Container( - padding: isFinalMatch - ? EdgeInsets.only(right: 16) - : EdgeInsets.symmetric(horizontal: 16, vertical: 24), + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 24), margin: EdgeInsets.symmetric(vertical: 8), decoration: BoxDecoration( color: context.colorScheme.surface, @@ -82,12 +80,6 @@ class _TournamentDetailOverviewTabState ), child: Row( children: [ - if (isFinalMatch) ...[ - RotatedBox( - quarterTurns: 7, - child: _finalTag(), - ), - ], _buildTeamInfo(team: match.teams.first.team), const Spacer(), Column( @@ -120,20 +112,6 @@ class _TournamentDetailOverviewTabState ); } - Widget _finalTag() { - return Container( - padding: EdgeInsets.symmetric(horizontal: 8, vertical: 2), - decoration: BoxDecoration( - color: context.colorScheme.primary.withOpacity(0.2), - borderRadius: BorderRadius.circular(30), - ), - child: Text( - "Semi Final 1", - style: TextStyle(fontFamily: AppTextStyle.poppinsFontFamily), - ), - ); - } - Widget _buildTeamInfo({ required TeamModel team, bool isSecond = false, diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index 5dca3c19..307d26c8 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -97,7 +97,7 @@ class _TournamentDetailScreenState pinned: true, delegate: SliverPersistentDelegate( child: _tabSelection(context), - size: 60, + size: 70, ), ), SliverFillRemaining( From d5935e33cac2359be7dac79932078dbdebc32c7b Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Fri, 18 Oct 2024 11:12:28 +0530 Subject: [PATCH 23/33] Fix padding issue --- .../detail/tabs/tournament_detail_overview_tab.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index 898664aa..f1e031af 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -35,8 +35,8 @@ class _TournamentDetailOverviewTabState return Container( color: context.colorScheme.containerLow, child: ListView( - padding: EdgeInsets.all(16) - .copyWith(bottom: context.mediaQueryPadding.bottom + 40), + padding: context.mediaQueryPadding.copyWith(top: 0) + + EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 40), children: [ _featuredMatchesView(context, widget.tournament.matches), _keyStatsView(context, widget.tournament.keyStats), From 2c08e89ea55c2967c784093fcb4d6f07b7cb665d Mon Sep 17 00:00:00 2001 From: sidhdhi canopas <122426509+cp-sidhdhi-p@users.noreply.github.com> Date: Fri, 18 Oct 2024 15:12:06 +0530 Subject: [PATCH 24/33] Add team in tournament (#120) * add team in tournament * refactor list view * verification list * minor change * height of sheet as per content * allow dismissing the sheet --- khelo/assets/locales/app_en.arb | 33 +- khelo/lib/components/create_team_cell.dart | 37 ++ khelo/lib/ui/app_route.dart | 20 +- khelo/lib/ui/flow/home/home_screen.dart | 2 +- .../components/bottom_sheet_wrapper.dart | 15 +- .../team/search_team/search_team_screen.dart | 40 +-- .../tournament/add/add_tournament_screen.dart | 23 +- .../add/add_tournament_view_model.dart | 7 + .../add_tournament_view_model.freezed.dart | 33 +- .../component/team_profile_cell.dart | 75 ++++ .../verification_team_list_sheet.dart | 136 ++++++++ .../team_selection/team_selection_screen.dart | 194 +++++++++++ .../team_selection_view_model.dart | 153 ++++++++ .../team_selection_view_model.freezed.dart | 329 ++++++++++++++++++ style/lib/widgets/adaptive_outlined_tile.dart | 4 +- style/lib/widgets/drag_handle.dart | 16 + 16 files changed, 1049 insertions(+), 68 deletions(-) create mode 100644 khelo/lib/components/create_team_cell.dart create mode 100644 khelo/lib/ui/flow/tournament/team_selection/component/team_profile_cell.dart create mode 100644 khelo/lib/ui/flow/tournament/team_selection/component/verification_team_list_sheet.dart create mode 100644 khelo/lib/ui/flow/tournament/team_selection/team_selection_screen.dart create mode 100644 khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart create mode 100644 khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.freezed.dart create mode 100644 style/lib/widgets/drag_handle.dart diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 2c11657b..6d106c24 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -32,10 +32,19 @@ "count": {} } }, + "common_players_title": "{count, plural, =0{{count} Players} =1{{count} Player} other{{count} Players}}", + "@common_players_title": { + "placeholders": { + "count": {} + } + }, "common_tie_title": "Tie", "common_wicket_taken_title": " Wicket taken", "common_matches_title": "Matches", "common_edit_team_title": "Edit team", + "common_create_team_title": "Create team", + "common_create_team_description": "Create Your Dream Cricket Team Today!", + "common_your_teams_title": "Your Teams", "common_location_title": "Location", "common_gender_title": "Gender", "common_batting_style_title": "Batting style", @@ -59,6 +68,7 @@ "common_second_inning_title": "Second inning", "common_qr_code_title": "QR Code", "common_verify_title": "Verify", + "common_verified_title": "Verified", "common_obscure_phone_number_text": "{countryCode} ***** ***{lastDigits}", "@common_obscure_phone_number_text": { "description": "+{countryCode} ***** ***{lastDigits}", @@ -117,7 +127,6 @@ "home_screen_set_up_match_title": "Set up a match in minutes.", "home_screen_create_match_btn": "Create match", "home_screen_set_up_team_title": "Set up a team in minutes.", - "home_screen_create_team_btn": "Create team", "home_screen_view_all_btn": "View all", "home_screen_no_matches_title": "No Matches in this area!", "home_screen_no_matches_description_text": "Enjoy the freedom of creating your own cricket matches or teams.", @@ -151,9 +160,17 @@ "add_tournament_type_placeholder": "Select tournament type", "add_tournament_start_date": "Start Date", "add_tournament_end_date": "End Date", + "add_tournament_team_selection": "Team selection", + "add_tournament_match_selection": "Match selection", "add_tournament_edit_banner": "Edit banner", "add_tournament_add_banner_placeholder": "Add banner image", "add_tournament_date_error": "End date should be greater than start date", + "add_tournament_teams_title": "{count, plural, =0{No teams} =1{{count} Team} other{{count} Teams}}", + "@add_tournament_teams_title": { + "placeholders": { + "count": {} + } + }, "tournament_list_empty_list_title": "No tournaments created", "tournament_list_empty_list_description": "Tap on the “ + ” icon to create a tournament", @@ -164,6 +181,10 @@ } }, + "team_selection_screen_title": "Select Teams", + "team_selection_select_team_title": "Select teams", + "team_selection_verify_team_to_add_title": "Verify teams to add", + "@_TOURNAMENT_DETAIL": { }, "tournament_detail_not_found_title": "No Tournament found", @@ -319,17 +340,9 @@ "search_user_empty_title": "Time to gather your officials!", "search_user_empty_description_text": "Search and assign officials for your next match. Get ready for cricket action!", "search_team_screen_title": "Search Team", - "search_team_your_teams_title": "Your Teams", "search_team_selection_error_text": "The team must have at least 2 players to add it in a match.", "search_team_search_placeholder_title": "Search team name", - "search_team_create_team_title": "Create team", - "search_team_create_team_description": "Create Your Dream Cricket Team Today!", - "search_team_player_title": "{count, plural, =0{{count} Players} =1{{count} Player} other{{count} Players}}", - "@search_team_player_title": { - "placeholders": { - "count": {} - } - }, + "power_play_text": "Power play {count}", "@power_play_text": { "description": "Power play {count}", diff --git a/khelo/lib/components/create_team_cell.dart b/khelo/lib/components/create_team_cell.dart new file mode 100644 index 00000000..98df4f9b --- /dev/null +++ b/khelo/lib/components/create_team_cell.dart @@ -0,0 +1,37 @@ +import 'package:flutter/cupertino.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:style/button/primary_button.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +import '../ui/app_route.dart'; + +Widget createTeamCell(BuildContext context) { + return Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + color: context.colorScheme.containerLow, + borderRadius: BorderRadius.circular(16), + border: Border.all(color: context.colorScheme.outline)), + child: Row( + children: [ + Expanded( + child: Text( + context.l10n.common_create_team_description, + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textPrimary), + ), + ), + const SizedBox(width: 16), + SizedBox( + height: 36, + child: PrimaryButton( + context.l10n.common_create_team_title, + expanded: false, + onPressed: () => AppRoute.addTeam().push(context), + ), + ), + ], + ), + ); +} diff --git a/khelo/lib/ui/app_route.dart b/khelo/lib/ui/app_route.dart index 16abb666..1367ec36 100644 --- a/khelo/lib/ui/app_route.dart +++ b/khelo/lib/ui/app_route.dart @@ -1,4 +1,5 @@ import 'dart:io'; + import 'package:data/api/match/match_model.dart'; import 'package:data/api/team/team_model.dart'; import 'package:flutter/cupertino.dart'; @@ -25,8 +26,9 @@ import 'package:khelo/ui/flow/team/detail/make_admin/make_team_admin_screen.dart import 'package:khelo/ui/flow/team/detail/team_detail_screen.dart'; import 'package:khelo/ui/flow/team/scanner/scanner_screen.dart'; import 'package:khelo/ui/flow/team/search_team/search_team_screen.dart'; - import 'package:khelo/ui/flow/tournament/add/add_tournament_screen.dart'; +import 'package:khelo/ui/flow/tournament/team_selection/team_selection_screen.dart'; + import 'flow/home/view_all/home_view_all_screen.dart'; import 'flow/main/main_screen.dart'; import 'flow/settings/support/contact_support_screen.dart'; @@ -56,6 +58,7 @@ class AppRoute { static const pathViewAll = "/view-all"; static const pathContactSelection = "/contact-selection"; static const pathAddTournament = "/add-tournament"; + static const pathTeamSelection = "/team-selection"; static const pathTournamentDetail = "/tournament-detail"; final String path; @@ -174,11 +177,16 @@ class AppRoute { builder: (_) => const AddTournamentScreen(), ); - static AppRoute tournamentDetail({required String tournamentId}) => AppRoute( - pathTournamentDetail, - builder: (_) => TournamentDetailScreen(tournamentId: tournamentId), + static AppRoute teamSelection({List? selectedTeams}) => AppRoute( + pathTeamSelection, + builder: (_) => TeamSelectionScreen(selectedTeams: selectedTeams), ); + static AppRoute tournamentDetail({required String tournamentId}) => AppRoute( + pathTournamentDetail, + builder: (_) => TournamentDetailScreen(tournamentId: tournamentId), + ); + static AppRoute matchDetailTab({required String matchId}) => AppRoute( pathMatchDetailTab, builder: (_) => MatchDetailTabScreen(matchId: matchId), @@ -337,6 +345,10 @@ class AppRoute { path: pathAddTournament, builder: (context, state) => state.widget(context), ), + GoRoute( + path: pathTeamSelection, + builder: (context, state) => state.widget(context), + ), GoRoute( path: pathTournamentDetail, builder: (context, state) => state.widget(context), diff --git a/khelo/lib/ui/flow/home/home_screen.dart b/khelo/lib/ui/flow/home/home_screen.dart index 1d5facc9..ead23c67 100644 --- a/khelo/lib/ui/flow/home/home_screen.dart +++ b/khelo/lib/ui/flow/home/home_screen.dart @@ -121,7 +121,7 @@ class _HomeScreenState extends ConsumerState { _createActionView( context, title: context.l10n.home_screen_set_up_team_title, - btnText: context.l10n.home_screen_create_team_btn, + btnText: context.l10n.common_create_team_title, onTap: () => AppRoute.addTeam().push(context), ), ], diff --git a/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart b/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart index 0ea5d750..6349a522 100644 --- a/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart +++ b/khelo/lib/ui/flow/score_board/components/bottom_sheet_wrapper.dart @@ -1,6 +1,7 @@ import 'package:flutter/cupertino.dart'; import 'package:style/button/bottom_sticky_overlay.dart'; import 'package:style/extensions/context_extensions.dart'; +import 'package:style/widgets/drag_handle.dart'; class BottomSheetWrapper extends StatelessWidget { final Widget content; @@ -41,19 +42,7 @@ class BottomSheetWrapper extends StatelessWidget { BottomStickyOverlay.padding, child: content), ), - if (showDragHandle) ...[ - Align( - alignment: Alignment.topCenter, - child: Container( - height: 4, - width: 32, - margin: const EdgeInsets.symmetric(vertical: 20), - decoration: BoxDecoration( - color: context.colorScheme.outline, - borderRadius: BorderRadius.circular(10)), - ), - ), - ], + if (showDragHandle) dragHandle(context), BottomStickyOverlay( child: Column( mainAxisSize: MainAxisSize.min, 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 873f1810..38bd49c1 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 @@ -10,16 +10,15 @@ import 'package:khelo/components/image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/string_extensions.dart'; import 'package:khelo/domain/extensions/widget_extension.dart'; -import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/team/search_team/components/team_member_sheet.dart'; import 'package:khelo/ui/flow/team/search_team/search_team_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; -import 'package:style/button/primary_button.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/text/search_text_field.dart'; +import '../../../../components/create_team_cell.dart'; import '../../../../gen/assets.gen.dart'; class SearchTeamScreen extends ConsumerStatefulWidget { @@ -140,7 +139,7 @@ class _SearchTeamScreenState extends ConsumerState { ], const SizedBox(height: 16), Text( - context.l10n.search_team_your_teams_title, + context.l10n.common_your_teams_title, style: AppTextStyle.header2 .copyWith(color: context.colorScheme.textSecondary), ), @@ -154,7 +153,7 @@ class _SearchTeamScreenState extends ConsumerState { ], ], const SizedBox(height: 8), - _createTeamCell(context) + createTeamCell(context) ], ); } @@ -202,8 +201,7 @@ class _SearchTeamScreenState extends ConsumerState { ), )), TextSpan( - text: context.l10n - .search_team_player_title(team.players.length), + text: context.l10n.common_players_title(team.players.length), ), ], ), @@ -216,36 +214,6 @@ class _SearchTeamScreenState extends ConsumerState { ); } - Widget _createTeamCell(BuildContext context) { - return Container( - padding: const EdgeInsets.all(16), - decoration: BoxDecoration( - color: context.colorScheme.containerLow, - borderRadius: BorderRadius.circular(16), - border: Border.all(color: context.colorScheme.outline)), - child: Row( - children: [ - Expanded( - child: Text( - context.l10n.search_team_create_team_description, - style: AppTextStyle.body2 - .copyWith(color: context.colorScheme.textPrimary), - ), - ), - const SizedBox(width: 16), - SizedBox( - height: 36, - child: PrimaryButton( - context.l10n.search_team_create_team_title, - expanded: false, - onPressed: () => AppRoute.addTeam().push(context), - ), - ), - ], - ), - ); - } - void _observeActionError() { ref.listen( searchTeamViewStateProvider.select((value) => value.showSelectionError), diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart index b0e1d184..3e0e4f17 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_screen.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:cached_network_image/cached_network_image.dart'; +import 'package:data/api/team/team_model.dart'; import 'package:data/api/tournament/tournament_model.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; @@ -10,6 +11,7 @@ import 'package:khelo/components/profile_image_avatar.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/enum_extensions.dart'; import 'package:khelo/gen/assets.gen.dart'; +import 'package:khelo/ui/app_route.dart'; import 'package:khelo/ui/flow/tournament/add/add_tournament_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/bottom_sticky_overlay.dart'; @@ -129,7 +131,26 @@ class _AddTournamentScreenState extends ConsumerState { }, ), const SizedBox(height: 16), - _dateScheduleView(context, state) + _dateScheduleView(context, state), + const SizedBox(height: 16), + AdaptiveOutlinedTile( + placeholder: context.l10n.add_tournament_team_selection, + headerText: context.l10n.add_tournament_team_selection, + title: state.teams.isEmpty + ? context.l10n.team_selection_select_team_title + : context.l10n + .add_tournament_teams_title(state.teams.length), + showTrailingIcon: true, + iconImage: Assets.images.icArrowForward, + onTap: () async { + final selectedTeams = + await AppRoute.teamSelection(selectedTeams: state.teams) + .push>(context); + if (context.mounted && selectedTeams != null) { + notifier.onSelectTeam(selectedTeams); + } + }, + ), ], ), ), diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart index eaf67e47..2c213c8c 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.dart @@ -1,5 +1,6 @@ import 'dart:io'; +import 'package:data/api/team/team_model.dart'; import 'package:data/api/tournament/tournament_model.dart'; import 'package:data/errors/app_error.dart'; import 'package:data/service/file_upload/file_upload_service.dart'; @@ -125,6 +126,7 @@ class AddTournamentViewNotifier extends StateNotifier { end_date: state.endDate, created_at: DateTime.now(), created_by: state.currentUserId!, + team_ids: state.teams.map((e) => e.id).toList(), members: [ TournamentMember( id: state.currentUserId!, @@ -146,6 +148,10 @@ class AddTournamentViewNotifier extends StateNotifier { } } + void onSelectTeam(List selectedTeams) { + state = state.copyWith(teams: selectedTeams); + } + @override void dispose() { state.nameController.dispose(); @@ -163,6 +169,7 @@ class AddTournamentState with _$AddTournamentState { String? currentUserId, required DateTime endDate, required DateTime startDate, + @Default([]) List teams, @Default(false) bool pop, @Default(false) bool loading, @Default(false) bool showDateError, diff --git a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart index a55c612f..61d55984 100644 --- a/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/add/add_tournament_view_model.freezed.dart @@ -23,6 +23,7 @@ mixin _$AddTournamentState { String? get currentUserId => throw _privateConstructorUsedError; DateTime get endDate => throw _privateConstructorUsedError; DateTime get startDate => throw _privateConstructorUsedError; + List get teams => throw _privateConstructorUsedError; bool get pop => throw _privateConstructorUsedError; bool get loading => throw _privateConstructorUsedError; bool get showDateError => throw _privateConstructorUsedError; @@ -55,6 +56,7 @@ abstract class $AddTournamentStateCopyWith<$Res> { String? currentUserId, DateTime endDate, DateTime startDate, + List teams, bool pop, bool loading, bool showDateError, @@ -88,6 +90,7 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> Object? currentUserId = freezed, Object? endDate = null, Object? startDate = null, + Object? teams = null, Object? pop = null, Object? loading = null, Object? showDateError = null, @@ -121,6 +124,10 @@ class _$AddTournamentStateCopyWithImpl<$Res, $Val extends AddTournamentState> ? _value.startDate : startDate // ignore: cast_nullable_to_non_nullable as DateTime, + teams: null == teams + ? _value.teams + : teams // ignore: cast_nullable_to_non_nullable + as List, pop: null == pop ? _value.pop : pop // ignore: cast_nullable_to_non_nullable @@ -177,6 +184,7 @@ abstract class _$$AddTournamentStateImplCopyWith<$Res> String? currentUserId, DateTime endDate, DateTime startDate, + List teams, bool pop, bool loading, bool showDateError, @@ -208,6 +216,7 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> Object? currentUserId = freezed, Object? endDate = null, Object? startDate = null, + Object? teams = null, Object? pop = null, Object? loading = null, Object? showDateError = null, @@ -241,6 +250,10 @@ class __$$AddTournamentStateImplCopyWithImpl<$Res> ? _value.startDate : startDate // ignore: cast_nullable_to_non_nullable as DateTime, + teams: null == teams + ? _value._teams + : teams // ignore: cast_nullable_to_non_nullable + as List, pop: null == pop ? _value.pop : pop // ignore: cast_nullable_to_non_nullable @@ -292,6 +305,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { this.currentUserId, required this.endDate, required this.startDate, + final List teams = const [], this.pop = false, this.loading = false, this.showDateError = false, @@ -300,7 +314,8 @@ class _$AddTournamentStateImpl implements _AddTournamentState { this.profileImgUrl = null, this.bannerImgUrl = null, required this.nameController, - this.selectedType = TournamentType.knockOut}); + this.selectedType = TournamentType.knockOut}) + : _teams = teams; @override final Object? error; @@ -316,6 +331,15 @@ class _$AddTournamentStateImpl implements _AddTournamentState { final DateTime endDate; @override final DateTime startDate; + final List _teams; + @override + @JsonKey() + List get teams { + if (_teams is EqualUnmodifiableListView) return _teams; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_teams); + } + @override @JsonKey() final bool pop; @@ -345,7 +369,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { @override String toString() { - return 'AddTournamentState(error: $error, actionError: $actionError, profilePath: $profilePath, bannerPath: $bannerPath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, pop: $pop, loading: $loading, showDateError: $showDateError, enableButton: $enableButton, imageUploading: $imageUploading, profileImgUrl: $profileImgUrl, bannerImgUrl: $bannerImgUrl, nameController: $nameController, selectedType: $selectedType)'; + return 'AddTournamentState(error: $error, actionError: $actionError, profilePath: $profilePath, bannerPath: $bannerPath, currentUserId: $currentUserId, endDate: $endDate, startDate: $startDate, teams: $teams, pop: $pop, loading: $loading, showDateError: $showDateError, enableButton: $enableButton, imageUploading: $imageUploading, profileImgUrl: $profileImgUrl, bannerImgUrl: $bannerImgUrl, nameController: $nameController, selectedType: $selectedType)'; } @override @@ -365,6 +389,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { (identical(other.endDate, endDate) || other.endDate == endDate) && (identical(other.startDate, startDate) || other.startDate == startDate) && + const DeepCollectionEquality().equals(other._teams, _teams) && (identical(other.pop, pop) || other.pop == pop) && (identical(other.loading, loading) || other.loading == loading) && (identical(other.showDateError, showDateError) || @@ -393,6 +418,7 @@ class _$AddTournamentStateImpl implements _AddTournamentState { currentUserId, endDate, startDate, + const DeepCollectionEquality().hash(_teams), pop, loading, showDateError, @@ -422,6 +448,7 @@ abstract class _AddTournamentState implements AddTournamentState { final String? currentUserId, required final DateTime endDate, required final DateTime startDate, + final List teams, final bool pop, final bool loading, final bool showDateError, @@ -447,6 +474,8 @@ abstract class _AddTournamentState implements AddTournamentState { @override DateTime get startDate; @override + List get teams; + @override bool get pop; @override bool get loading; diff --git a/khelo/lib/ui/flow/tournament/team_selection/component/team_profile_cell.dart b/khelo/lib/ui/flow/tournament/team_selection/component/team_profile_cell.dart new file mode 100644 index 00000000..bd73619b --- /dev/null +++ b/khelo/lib/ui/flow/tournament/team_selection/component/team_profile_cell.dart @@ -0,0 +1,75 @@ +import 'package:data/api/team/team_model.dart'; +import 'package:flutter/material.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/string_extensions.dart'; +import 'package:style/animations/on_tap_scale.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +import '../../../../../components/image_avatar.dart'; + +class TeamProfileCell extends StatelessWidget { + final TeamModel team; + final Widget? trailing; + final Function()? onTap; + final Function()? onLongTap; + + const TeamProfileCell({ + super.key, + required this.team, + this.trailing, + this.onTap, + this.onLongTap, + }); + + @override + Widget build(BuildContext context) { + return OnTapScale( + onTap: onTap, + onLongTap: onLongTap, + child: Material( + type: MaterialType.transparency, + child: ListTile( + dense: true, + contentPadding: EdgeInsets.zero, + leading: ImageAvatar( + initial: team.name_initial ?? team.name.initials(limit: 1), + imageUrl: team.profile_img_url, + size: 40, + ), + title: Text( + team.name, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + subtitle: Text.rich( + TextSpan( + text: team.city != null + ? team.city! + : context.l10n.common_not_specified_title, + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textSecondary), + children: [ + WidgetSpan( + alignment: PlaceholderAlignment.middle, + child: Container( + margin: const EdgeInsets.symmetric(horizontal: 8), + constraints: + const BoxConstraints(maxHeight: 4, maxWidth: 4), + decoration: BoxDecoration( + shape: BoxShape.circle, + color: context.colorScheme.textDisabled, + ), + )), + TextSpan( + text: context.l10n.common_players_title(team.players.length), + ), + ], + ), + ), + trailing: trailing, + ), + ), + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/team_selection/component/verification_team_list_sheet.dart b/khelo/lib/ui/flow/tournament/team_selection/component/verification_team_list_sheet.dart new file mode 100644 index 00000000..c6769cbf --- /dev/null +++ b/khelo/lib/ui/flow/tournament/team_selection/component/verification_team_list_sheet.dart @@ -0,0 +1,136 @@ +import 'package:data/api/team/team_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:go_router/go_router.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/ui/flow/tournament/team_selection/component/team_profile_cell.dart'; +import 'package:style/button/bottom_sticky_overlay.dart'; +import 'package:style/button/primary_button.dart'; +import 'package:style/button/secondary_button.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +import '../../../team/search_team/components/team_member_sheet.dart'; + +class VerificationTeamListSheet extends StatefulWidget { + final List verified; + final List allTeams; + + static Future show( + BuildContext context, { + required List verified, + required List allTeams, + bool isForCreateUser = false, + }) { + HapticFeedback.mediumImpact(); + return showModalBottomSheet( + context: context, + isScrollControlled: true, + enableDrag: false, + showDragHandle: true, + useRootNavigator: true, + backgroundColor: context.colorScheme.surface, + builder: (context) { + return VerificationTeamListSheet( + verified: verified, + allTeams: allTeams, + ); + }, + ); + } + + const VerificationTeamListSheet({ + super.key, + required this.verified, + required this.allTeams, + }); + + @override + State createState() => + _VerificationTeamListSheetState(); +} + +class _VerificationTeamListSheetState extends State { + late final List verified; + + @override + void initState() { + verified = widget.verified; + super.initState(); + } + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: + BoxConstraints(maxHeight: context.mediaQuerySize.height * 0.8), + child: IntrinsicHeight( + child: Stack( + children: [ + Padding( + padding: context.mediaQueryPadding + + BottomStickyOverlay.padding + + EdgeInsets.symmetric(horizontal: 16), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + context.l10n.team_selection_verify_team_to_add_title, + style: AppTextStyle.header3 + .copyWith(color: context.colorScheme.textPrimary), + ), + SizedBox(height: 24), + ..._buildList(context), + SizedBox(height: 40), + ], + ), + ), + ), + BottomStickyOverlay( + child: PrimaryButton(context.l10n.common_select_title, + enabled: verified.isNotEmpty, + onPressed: () => context.pop(verified.toSet().toList())), + ), + ], + ), + ), + ); + } + + List _buildList(BuildContext context) { + if (widget.allTeams.isEmpty) { + return []; + } + return List.generate((widget.allTeams.length * 2) - 1, (index) { + if (index.isOdd) { + return Divider(color: context.colorScheme.outline); + } else { + final team = widget.allTeams[index ~/ 2]; + final isVerified = verified.map((e) => e.id).contains(team.id); + return TeamProfileCell( + team: team, + trailing: SecondaryButton( + isVerified + ? context.l10n.common_verified_title + : context.l10n.common_verify_title, + enabled: !isVerified, + onPressed: () async { + final res = await TeamMemberSheet.show( + context, + team: team, + isForVerification: true, + ); + + if (res == true && context.mounted) { + verified.add(team); + setState(() {}); + } + }, + ), + onTap: () => TeamMemberSheet.show(context, team: team), + ); + } + }); + } +} diff --git a/khelo/lib/ui/flow/tournament/team_selection/team_selection_screen.dart b/khelo/lib/ui/flow/tournament/team_selection/team_selection_screen.dart new file mode 100644 index 00000000..cfafa536 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/team_selection/team_selection_screen.dart @@ -0,0 +1,194 @@ +import 'package:data/api/team/team_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:go_router/go_router.dart'; +import 'package:khelo/components/app_page.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/ui/flow/tournament/team_selection/component/verification_team_list_sheet.dart'; +import 'package:khelo/ui/flow/tournament/team_selection/team_selection_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'; +import 'package:style/text/search_text_field.dart'; +import 'package:style/widgets/rounded_check_box.dart'; + +import '../../../../components/create_team_cell.dart'; +import '../../../../components/error_screen.dart'; +import '../../../../components/error_snackbar.dart'; +import '../../../../domain/extensions/widget_extension.dart'; +import '../../../../gen/assets.gen.dart'; +import '../../team/search_team/components/team_member_sheet.dart'; +import 'component/team_profile_cell.dart'; + +class TeamSelectionScreen extends ConsumerStatefulWidget { + final List? selectedTeams; + + const TeamSelectionScreen({ + super.key, + this.selectedTeams, + }); + + @override + ConsumerState createState() => _TeamSelectionScreenState(); +} + +class _TeamSelectionScreenState extends ConsumerState { + late TeamSelectionViewNotifier notifier; + + @override + void initState() { + super.initState(); + notifier = ref.read(teamSelectionViewStateProvider.notifier); + runPostFrame(() => notifier.setData(widget.selectedTeams)); + } + + @override + Widget build(BuildContext context) { + final state = ref.watch(teamSelectionViewStateProvider); + _observeActionError(); + + return AppPage( + title: context.l10n.team_selection_screen_title, + actions: [ + IconButton( + onPressed: () async { + final userTeams = state.userTeams.map((e) => e.id).toSet(); + final previouslySelected = notifier.selectedIds.toSet(); + final List verified = []; + final List unverified = []; + + for (final team in state.selectedTeams) { + if (userTeams.contains(team.id) || + previouslySelected.contains(team.id)) { + verified.add(team); + } else { + unverified.add(team); + } + } + + if (unverified.isNotEmpty) { + final list = + await VerificationTeamListSheet.show>( + context, + verified: verified, + allTeams: state.selectedTeams, + ); + if (context.mounted && list != null) context.pop(list); + } else if (context.mounted) { + context.pop(verified); + } + }, + icon: SvgPicture.asset( + Assets.images.icCheck, + colorFilter: ColorFilter.mode( + context.colorScheme.textPrimary, + BlendMode.srcIn, + ), + ), + ), + ], + body: SafeArea( + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Column( + children: [ + _searchTextField(context, state), + Expanded(child: _teamList(context, state)), + ], + ), + ), + ), + ); + } + + Widget _searchTextField(BuildContext context, TeamSelectionViewState state) { + return Padding( + padding: const EdgeInsets.symmetric(vertical: 8), + child: SearchTextField( + controller: state.searchController, + onChange: notifier.onSearchChanged, + hintText: context.l10n.search_team_search_placeholder_title, + suffixIcon: state.searchInProgress + ? const UnconstrainedBox(child: AppProgressIndicator(radius: 8)) + : const SizedBox(), + ), + ); + } + + Widget _teamList(BuildContext context, TeamSelectionViewState state) { + if (state.loading) { + return const Center(child: AppProgressIndicator()); + } + + if (state.error != null) { + return ErrorScreen( + error: state.error, + onRetryTap: notifier.onSearchChanged, + ); + } + + final selectedTeam = notifier.getSelectedTeamOfOtherUser(); + + return ListView( + padding: const EdgeInsets.symmetric(vertical: 16), + children: [ + ..._buildTeamList(state, + teams: state.searchResults, + showDividerAtLast: selectedTeam.isNotEmpty), + ..._buildTeamList(state, teams: selectedTeam), + const SizedBox(height: 16), + Text( + context.l10n.common_your_teams_title, + style: AppTextStyle.header2 + .copyWith(color: context.colorScheme.textSecondary), + ), + const SizedBox(height: 8), + ..._buildTeamList(state, teams: state.userTeams), + SizedBox(height: state.userTeams.isEmpty ? 8 : 16), + createTeamCell(context) + ], + ); + } + + List _buildTeamList( + TeamSelectionViewState state, { + required List teams, + bool showDividerAtLast = false, + }) { + if (teams.isEmpty) { + return []; + } + final listLength = (teams.length * 2) - (showDividerAtLast ? 0 : 1); + return List.generate(listLength, (index) { + if (index.isOdd) { + return Divider(color: context.colorScheme.outline); + } else { + final team = teams[index ~/ 2]; + return TeamProfileCell( + team: team, + onTap: () => notifier.onTeamCellTap(team), + onLongTap: () => TeamMemberSheet.show(context, team: team), + trailing: RoundedCheckBox( + isSelected: + state.selectedTeams.map((e) => e.id).contains(team.id), + onTap: (_) => notifier.onTeamCellTap(team)), + ); + } + }); + } + + void _observeActionError() { + ref.listen( + teamSelectionViewStateProvider + .select((value) => value.showSelectionError), + (previous, next) { + if (next) { + showErrorSnackBar( + context: context, + error: context.l10n.search_team_selection_error_text); + } + }, + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart new file mode 100644 index 00000000..4dab3f2e --- /dev/null +++ b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.dart @@ -0,0 +1,153 @@ +import 'dart:async'; + +import 'package:data/api/team/team_model.dart'; +import 'package:data/service/team/team_service.dart'; +import 'package:data/storage/app_preferences.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'team_selection_view_model.freezed.dart'; + +final teamSelectionViewStateProvider = StateNotifierProvider.autoDispose< + TeamSelectionViewNotifier, TeamSelectionViewState>( + (ref) { + final notifier = TeamSelectionViewNotifier( + ref.read(teamServiceProvider), + ref.read(currentUserPod)?.id, + ); + ref.listen(currentUserPod, (previous, next) { + notifier._setUserId(next?.id); + }); + return notifier; + }, +); + +class TeamSelectionViewNotifier extends StateNotifier { + final TeamService _teamService; + StreamSubscription? _streamSubscription; + String? _currentUserId; + Timer? _debounce; + + List selectedIds = []; + + TeamSelectionViewNotifier(this._teamService, this._currentUserId) + : super( + TeamSelectionViewState(searchController: TextEditingController())) { + _loadTeamList(); + } + + void _setUserId(String? userId) { + if (userId == null) { + _streamSubscription?.cancel(); + } + _currentUserId = userId; + _loadTeamList(); + } + + void setData(List? teams) { + state = state.copyWith(selectedTeams: teams ?? []); + selectedIds = teams?.map((e) => e.id).toList() ?? []; + } + + Future _loadTeamList() async { + if (_currentUserId == null) return; + _streamSubscription?.cancel(); + state = state.copyWith(loading: true); + _streamSubscription = + _teamService.streamUserOwnedTeams(_currentUserId!).listen((teams) { + state = state.copyWith(userTeams: teams, loading: false, error: null); + }, onError: (e) { + state = state.copyWith(loading: false, error: e); + debugPrint( + "TeamSelectionViewNotifier: error while loading team list -> $e"); + }); + } + + Future _search(String searchKey) async { + state = state.copyWith(searchInProgress: true); + + try { + final teams = await _teamService.searchTeam(searchKey); + final selectedTeam = state.selectedTeams.map((e) => e.id); + final filteredTeam = + teams.where((e) => !selectedTeam.contains(e.id)).toList(); + + state = state.copyWith( + searchResults: filteredTeam, + error: null, + searchInProgress: false, + ); + } catch (e) { + state = state.copyWith( + searchInProgress: false, + error: e, + ); + debugPrint("TeamSelectionViewNotifier: error while searching team -> $e"); + } + } + + void onSearchChanged() { + if (_debounce != null && _debounce!.isActive) { + _debounce!.cancel(); + } + + _debounce = Timer(const Duration(milliseconds: 500), () async { + if (state.searchController.text.isNotEmpty) { + _search(state.searchController.text.trim()); + } else { + state = state.copyWith(searchResults: []); + } + }); + } + + void onTeamCellTap(TeamModel team) { + state = state.copyWith(showSelectionError: false); + final playersCount = + (team.players.where((player) => player.user.isActive).toList()).length; + if (playersCount >= 2) { + final isAlreadySelected = + state.selectedTeams.map((e) => e.id).contains(team.id); + + final teams = state.selectedTeams.toList(); + if (isAlreadySelected) { + teams.removeWhere((e) => e.id == team.id); + } else { + teams.add(team); + } + state = state.copyWith(selectedTeams: teams); + } else { + state = state.copyWith(showSelectionError: true); + } + } + + List getSelectedTeamOfOtherUser() { + final userTeams = state.userTeams.map((e) => e.id); + final searchedTeam = state.searchResults.map((e) => e.id); + return state.selectedTeams + .where((e) => !userTeams.contains(e.id) && !searchedTeam.contains(e.id)) + .toList(); + } + + @override + void dispose() { + state.searchController.dispose(); + _debounce?.cancel(); + _streamSubscription?.cancel(); + super.dispose(); + } +} + +@freezed +class TeamSelectionViewState with _$TeamSelectionViewState { + const factory TeamSelectionViewState({ + required TextEditingController searchController, + Object? error, + @Default([]) List selectedTeams, + @Default([]) List searchResults, + @Default([]) List userTeams, + @Default(false) bool loading, + @Default(false) bool showSelectionError, + @Default(false) bool searchInProgress, + }) = _TeamSelectionViewState; +} diff --git a/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.freezed.dart new file mode 100644 index 00000000..0845d108 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/team_selection/team_selection_view_model.freezed.dart @@ -0,0 +1,329 @@ +// 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 'team_selection_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 _$TeamSelectionViewState { + TextEditingController get searchController => + throw _privateConstructorUsedError; + Object? get error => throw _privateConstructorUsedError; + List get selectedTeams => throw _privateConstructorUsedError; + List get searchResults => throw _privateConstructorUsedError; + List get userTeams => throw _privateConstructorUsedError; + bool get loading => throw _privateConstructorUsedError; + bool get showSelectionError => throw _privateConstructorUsedError; + bool get searchInProgress => throw _privateConstructorUsedError; + + /// Create a copy of TeamSelectionViewState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + $TeamSelectionViewStateCopyWith get copyWith => + throw _privateConstructorUsedError; +} + +/// @nodoc +abstract class $TeamSelectionViewStateCopyWith<$Res> { + factory $TeamSelectionViewStateCopyWith(TeamSelectionViewState value, + $Res Function(TeamSelectionViewState) then) = + _$TeamSelectionViewStateCopyWithImpl<$Res, TeamSelectionViewState>; + @useResult + $Res call( + {TextEditingController searchController, + Object? error, + List selectedTeams, + List searchResults, + List userTeams, + bool loading, + bool showSelectionError, + bool searchInProgress}); +} + +/// @nodoc +class _$TeamSelectionViewStateCopyWithImpl<$Res, + $Val extends TeamSelectionViewState> + implements $TeamSelectionViewStateCopyWith<$Res> { + _$TeamSelectionViewStateCopyWithImpl(this._value, this._then); + + // ignore: unused_field + final $Val _value; + // ignore: unused_field + final $Res Function($Val) _then; + + /// Create a copy of TeamSelectionViewState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? searchController = null, + Object? error = freezed, + Object? selectedTeams = null, + Object? searchResults = null, + Object? userTeams = null, + Object? loading = null, + Object? showSelectionError = null, + Object? searchInProgress = null, + }) { + return _then(_value.copyWith( + searchController: null == searchController + ? _value.searchController + : searchController // ignore: cast_nullable_to_non_nullable + as TextEditingController, + error: freezed == error ? _value.error : error, + selectedTeams: null == selectedTeams + ? _value.selectedTeams + : selectedTeams // ignore: cast_nullable_to_non_nullable + as List, + searchResults: null == searchResults + ? _value.searchResults + : searchResults // ignore: cast_nullable_to_non_nullable + as List, + userTeams: null == userTeams + ? _value.userTeams + : userTeams // ignore: cast_nullable_to_non_nullable + as List, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + showSelectionError: null == showSelectionError + ? _value.showSelectionError + : showSelectionError // ignore: cast_nullable_to_non_nullable + as bool, + searchInProgress: null == searchInProgress + ? _value.searchInProgress + : searchInProgress // ignore: cast_nullable_to_non_nullable + as bool, + ) as $Val); + } +} + +/// @nodoc +abstract class _$$TeamSelectionViewStateImplCopyWith<$Res> + implements $TeamSelectionViewStateCopyWith<$Res> { + factory _$$TeamSelectionViewStateImplCopyWith( + _$TeamSelectionViewStateImpl value, + $Res Function(_$TeamSelectionViewStateImpl) then) = + __$$TeamSelectionViewStateImplCopyWithImpl<$Res>; + @override + @useResult + $Res call( + {TextEditingController searchController, + Object? error, + List selectedTeams, + List searchResults, + List userTeams, + bool loading, + bool showSelectionError, + bool searchInProgress}); +} + +/// @nodoc +class __$$TeamSelectionViewStateImplCopyWithImpl<$Res> + extends _$TeamSelectionViewStateCopyWithImpl<$Res, + _$TeamSelectionViewStateImpl> + implements _$$TeamSelectionViewStateImplCopyWith<$Res> { + __$$TeamSelectionViewStateImplCopyWithImpl( + _$TeamSelectionViewStateImpl _value, + $Res Function(_$TeamSelectionViewStateImpl) _then) + : super(_value, _then); + + /// Create a copy of TeamSelectionViewState + /// with the given fields replaced by the non-null parameter values. + @pragma('vm:prefer-inline') + @override + $Res call({ + Object? searchController = null, + Object? error = freezed, + Object? selectedTeams = null, + Object? searchResults = null, + Object? userTeams = null, + Object? loading = null, + Object? showSelectionError = null, + Object? searchInProgress = null, + }) { + return _then(_$TeamSelectionViewStateImpl( + searchController: null == searchController + ? _value.searchController + : searchController // ignore: cast_nullable_to_non_nullable + as TextEditingController, + error: freezed == error ? _value.error : error, + selectedTeams: null == selectedTeams + ? _value._selectedTeams + : selectedTeams // ignore: cast_nullable_to_non_nullable + as List, + searchResults: null == searchResults + ? _value._searchResults + : searchResults // ignore: cast_nullable_to_non_nullable + as List, + userTeams: null == userTeams + ? _value._userTeams + : userTeams // ignore: cast_nullable_to_non_nullable + as List, + loading: null == loading + ? _value.loading + : loading // ignore: cast_nullable_to_non_nullable + as bool, + showSelectionError: null == showSelectionError + ? _value.showSelectionError + : showSelectionError // ignore: cast_nullable_to_non_nullable + as bool, + searchInProgress: null == searchInProgress + ? _value.searchInProgress + : searchInProgress // ignore: cast_nullable_to_non_nullable + as bool, + )); + } +} + +/// @nodoc + +class _$TeamSelectionViewStateImpl implements _TeamSelectionViewState { + const _$TeamSelectionViewStateImpl( + {required this.searchController, + this.error, + final List selectedTeams = const [], + final List searchResults = const [], + final List userTeams = const [], + this.loading = false, + this.showSelectionError = false, + this.searchInProgress = false}) + : _selectedTeams = selectedTeams, + _searchResults = searchResults, + _userTeams = userTeams; + + @override + final TextEditingController searchController; + @override + final Object? error; + final List _selectedTeams; + @override + @JsonKey() + List get selectedTeams { + if (_selectedTeams is EqualUnmodifiableListView) return _selectedTeams; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_selectedTeams); + } + + final List _searchResults; + @override + @JsonKey() + List get searchResults { + if (_searchResults is EqualUnmodifiableListView) return _searchResults; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_searchResults); + } + + final List _userTeams; + @override + @JsonKey() + List get userTeams { + if (_userTeams is EqualUnmodifiableListView) return _userTeams; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_userTeams); + } + + @override + @JsonKey() + final bool loading; + @override + @JsonKey() + final bool showSelectionError; + @override + @JsonKey() + final bool searchInProgress; + + @override + String toString() { + return 'TeamSelectionViewState(searchController: $searchController, error: $error, selectedTeams: $selectedTeams, searchResults: $searchResults, userTeams: $userTeams, loading: $loading, showSelectionError: $showSelectionError, searchInProgress: $searchInProgress)'; + } + + @override + bool operator ==(Object other) { + return identical(this, other) || + (other.runtimeType == runtimeType && + other is _$TeamSelectionViewStateImpl && + (identical(other.searchController, searchController) || + other.searchController == searchController) && + const DeepCollectionEquality().equals(other.error, error) && + const DeepCollectionEquality() + .equals(other._selectedTeams, _selectedTeams) && + const DeepCollectionEquality() + .equals(other._searchResults, _searchResults) && + const DeepCollectionEquality() + .equals(other._userTeams, _userTeams) && + (identical(other.loading, loading) || other.loading == loading) && + (identical(other.showSelectionError, showSelectionError) || + other.showSelectionError == showSelectionError) && + (identical(other.searchInProgress, searchInProgress) || + other.searchInProgress == searchInProgress)); + } + + @override + int get hashCode => Object.hash( + runtimeType, + searchController, + const DeepCollectionEquality().hash(error), + const DeepCollectionEquality().hash(_selectedTeams), + const DeepCollectionEquality().hash(_searchResults), + const DeepCollectionEquality().hash(_userTeams), + loading, + showSelectionError, + searchInProgress); + + /// Create a copy of TeamSelectionViewState + /// with the given fields replaced by the non-null parameter values. + @JsonKey(includeFromJson: false, includeToJson: false) + @override + @pragma('vm:prefer-inline') + _$$TeamSelectionViewStateImplCopyWith<_$TeamSelectionViewStateImpl> + get copyWith => __$$TeamSelectionViewStateImplCopyWithImpl< + _$TeamSelectionViewStateImpl>(this, _$identity); +} + +abstract class _TeamSelectionViewState implements TeamSelectionViewState { + const factory _TeamSelectionViewState( + {required final TextEditingController searchController, + final Object? error, + final List selectedTeams, + final List searchResults, + final List userTeams, + final bool loading, + final bool showSelectionError, + final bool searchInProgress}) = _$TeamSelectionViewStateImpl; + + @override + TextEditingController get searchController; + @override + Object? get error; + @override + List get selectedTeams; + @override + List get searchResults; + @override + List get userTeams; + @override + bool get loading; + @override + bool get showSelectionError; + @override + bool get searchInProgress; + + /// Create a copy of TeamSelectionViewState + /// with the given fields replaced by the non-null parameter values. + @override + @JsonKey(includeFromJson: false, includeToJson: false) + _$$TeamSelectionViewStateImplCopyWith<_$TeamSelectionViewStateImpl> + get copyWith => throw _privateConstructorUsedError; +} diff --git a/style/lib/widgets/adaptive_outlined_tile.dart b/style/lib/widgets/adaptive_outlined_tile.dart index 11a9b332..66aa72a8 100644 --- a/style/lib/widgets/adaptive_outlined_tile.dart +++ b/style/lib/widgets/adaptive_outlined_tile.dart @@ -11,6 +11,7 @@ class AdaptiveOutlinedTile extends StatelessWidget { final int? maxLines; final String placeholder; final bool showTrailingIcon; + final String? iconImage; final Function()? onTap; const AdaptiveOutlinedTile({ @@ -21,6 +22,7 @@ class AdaptiveOutlinedTile extends StatelessWidget { this.maxLines, required this.placeholder, this.showTrailingIcon = false, + this.iconImage, this.onTap, }); @@ -73,7 +75,7 @@ class AdaptiveOutlinedTile extends StatelessWidget { ), if (showTrailingIcon) ...[ SvgPicture.asset( - "assets/images/ic_arrow_down.svg", + iconImage ?? "assets/images/ic_arrow_down.svg", height: 18, width: 18, colorFilter: ColorFilter.mode( diff --git a/style/lib/widgets/drag_handle.dart b/style/lib/widgets/drag_handle.dart new file mode 100644 index 00000000..d2e0bbb0 --- /dev/null +++ b/style/lib/widgets/drag_handle.dart @@ -0,0 +1,16 @@ +import 'package:flutter/cupertino.dart'; +import 'package:style/extensions/context_extensions.dart'; + +Widget dragHandle(BuildContext context) { + return Align( + alignment: Alignment.topCenter, + child: Container( + height: 4, + width: 32, + margin: const EdgeInsets.symmetric(vertical: 20), + decoration: BoxDecoration( + color: context.colorScheme.outline, + borderRadius: BorderRadius.circular(10)), + ), + ); +} From 51e2e735125365bde6ab2cf0b1b77d0cf15fc8d6 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Mon, 21 Oct 2024 16:01:42 +0530 Subject: [PATCH 25/33] Mr changes --- data/lib/api/ball_score/ball_score_model.dart | 12 +++ .../ball_score/ball_score_model.freezed.dart | 92 +++++++++++++++- .../api/ball_score/ball_score_model.g.dart | 90 ++++++++++++++++ data/lib/api/tournament/tournament_model.dart | 81 +++++++++++++- .../tournament/tournament_model.freezed.dart | 102 ++++++++++++++---- .../api/tournament/tournament_model.g.dart | 15 ++- data/lib/service/match/match_service.dart | 51 +++++---- .../tournament/tournament_service.dart | 42 ++++---- khelo/assets/locales/app_en.arb | 11 +- khelo/lib/components/won_by_message_text.dart | 4 +- .../domain/extensions/enum_extensions.dart | 15 +++ .../lib/domain/formatter/date_formatter.dart | 4 +- .../tabs/tournament_detail_overview_tab.dart | 16 +-- .../detail/tournament_detail_screen.dart | 69 +++++++----- .../tournament/tournament_list_screen.dart | 2 + 15 files changed, 496 insertions(+), 110 deletions(-) diff --git a/data/lib/api/ball_score/ball_score_model.dart b/data/lib/api/ball_score/ball_score_model.dart index b96f83e4..c951d664 100644 --- a/data/lib/api/ball_score/ball_score_model.dart +++ b/data/lib/api/ball_score/ball_score_model.dart @@ -141,6 +141,9 @@ class UserStat with _$UserStat { BowlingStat? bowlingStat, FieldingStat? fieldingStat, }) = _UserStat; + + factory UserStat.fromJson(Map json) => + _$UserStatFromJson(json); } @freezed @@ -157,6 +160,9 @@ class BattingStat with _$BattingStat { @Default(0) int hundreds, @Default(0) int ducks, }) = _BattingStat; + + factory BattingStat.fromJson(Map json) => + _$BattingStatFromJson(json); } @freezed @@ -173,6 +179,9 @@ class BowlingStat with _$BowlingStat { @Default(0.0) double strikeRate, @Default(0.0) double economyRate, }) = _BowlingStat; + + factory BowlingStat.fromJson(Map json) => + _$BowlingStatFromJson(json); } @freezed @@ -182,6 +191,9 @@ class FieldingStat with _$FieldingStat { @Default(0) int runOut, @Default(0) int stumping, }) = _FieldingStat; + + factory FieldingStat.fromJson(Map json) => + _$FieldingStatFromJson(json); } @freezed diff --git a/data/lib/api/ball_score/ball_score_model.freezed.dart b/data/lib/api/ball_score/ball_score_model.freezed.dart index fc687cb4..06a0ae2a 100644 --- a/data/lib/api/ball_score/ball_score_model.freezed.dart +++ b/data/lib/api/ball_score/ball_score_model.freezed.dart @@ -569,12 +569,19 @@ abstract class _BallScoreModel implements BallScoreModel { throw _privateConstructorUsedError; } +UserStat _$UserStatFromJson(Map json) { + return _UserStat.fromJson(json); +} + /// @nodoc mixin _$UserStat { BattingStat? get battingStat => throw _privateConstructorUsedError; BowlingStat? get bowlingStat => throw _privateConstructorUsedError; FieldingStat? get fieldingStat => throw _privateConstructorUsedError; + /// Serializes this UserStat to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + /// Create a copy of UserStat /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -731,10 +738,13 @@ class __$$UserStatImplCopyWithImpl<$Res> } /// @nodoc - +@JsonSerializable() class _$UserStatImpl implements _UserStat { const _$UserStatImpl({this.battingStat, this.bowlingStat, this.fieldingStat}); + factory _$UserStatImpl.fromJson(Map json) => + _$$UserStatImplFromJson(json); + @override final BattingStat? battingStat; @override @@ -760,6 +770,7 @@ class _$UserStatImpl implements _UserStat { other.fieldingStat == fieldingStat)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, battingStat, bowlingStat, fieldingStat); @@ -771,6 +782,13 @@ class _$UserStatImpl implements _UserStat { @pragma('vm:prefer-inline') _$$UserStatImplCopyWith<_$UserStatImpl> get copyWith => __$$UserStatImplCopyWithImpl<_$UserStatImpl>(this, _$identity); + + @override + Map toJson() { + return _$$UserStatImplToJson( + this, + ); + } } abstract class _UserStat implements UserStat { @@ -779,6 +797,9 @@ abstract class _UserStat implements UserStat { final BowlingStat? bowlingStat, final FieldingStat? fieldingStat}) = _$UserStatImpl; + factory _UserStat.fromJson(Map json) = + _$UserStatImpl.fromJson; + @override BattingStat? get battingStat; @override @@ -794,6 +815,10 @@ abstract class _UserStat implements UserStat { throw _privateConstructorUsedError; } +BattingStat _$BattingStatFromJson(Map json) { + return _BattingStat.fromJson(json); +} + /// @nodoc mixin _$BattingStat { int get innings => throw _privateConstructorUsedError; @@ -807,6 +832,9 @@ mixin _$BattingStat { int get hundreds => throw _privateConstructorUsedError; int get ducks => throw _privateConstructorUsedError; + /// Serializes this BattingStat to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + /// Create a copy of BattingStat /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -995,7 +1023,7 @@ class __$$BattingStatImplCopyWithImpl<$Res> } /// @nodoc - +@JsonSerializable() class _$BattingStatImpl implements _BattingStat { const _$BattingStatImpl( {this.innings = 0, @@ -1009,6 +1037,9 @@ class _$BattingStatImpl implements _BattingStat { this.hundreds = 0, this.ducks = 0}); + factory _$BattingStatImpl.fromJson(Map json) => + _$$BattingStatImplFromJson(json); + @override @JsonKey() final int innings; @@ -1066,6 +1097,7 @@ class _$BattingStatImpl implements _BattingStat { (identical(other.ducks, ducks) || other.ducks == ducks)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, innings, runScored, average, strikeRate, ballFaced, fours, sixes, fifties, hundreds, ducks); @@ -1077,6 +1109,13 @@ class _$BattingStatImpl implements _BattingStat { @pragma('vm:prefer-inline') _$$BattingStatImplCopyWith<_$BattingStatImpl> get copyWith => __$$BattingStatImplCopyWithImpl<_$BattingStatImpl>(this, _$identity); + + @override + Map toJson() { + return _$$BattingStatImplToJson( + this, + ); + } } abstract class _BattingStat implements BattingStat { @@ -1092,6 +1131,9 @@ abstract class _BattingStat implements BattingStat { final int hundreds, final int ducks}) = _$BattingStatImpl; + factory _BattingStat.fromJson(Map json) = + _$BattingStatImpl.fromJson; + @override int get innings; @override @@ -1121,6 +1163,10 @@ abstract class _BattingStat implements BattingStat { throw _privateConstructorUsedError; } +BowlingStat _$BowlingStatFromJson(Map json) { + return _BowlingStat.fromJson(json); +} + /// @nodoc mixin _$BowlingStat { int get innings => throw _privateConstructorUsedError; @@ -1134,6 +1180,9 @@ mixin _$BowlingStat { double get strikeRate => throw _privateConstructorUsedError; double get economyRate => throw _privateConstructorUsedError; + /// Serializes this BowlingStat to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + /// Create a copy of BowlingStat /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -1322,7 +1371,7 @@ class __$$BowlingStatImplCopyWithImpl<$Res> } /// @nodoc - +@JsonSerializable() class _$BowlingStatImpl implements _BowlingStat { const _$BowlingStatImpl( {this.innings = 0, @@ -1336,6 +1385,9 @@ class _$BowlingStatImpl implements _BowlingStat { this.strikeRate = 0.0, this.economyRate = 0.0}); + factory _$BowlingStatImpl.fromJson(Map json) => + _$$BowlingStatImplFromJson(json); + @override @JsonKey() final int innings; @@ -1394,6 +1446,7 @@ class _$BowlingStatImpl implements _BowlingStat { other.economyRate == economyRate)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash( runtimeType, @@ -1415,6 +1468,13 @@ class _$BowlingStatImpl implements _BowlingStat { @pragma('vm:prefer-inline') _$$BowlingStatImplCopyWith<_$BowlingStatImpl> get copyWith => __$$BowlingStatImplCopyWithImpl<_$BowlingStatImpl>(this, _$identity); + + @override + Map toJson() { + return _$$BowlingStatImplToJson( + this, + ); + } } abstract class _BowlingStat implements BowlingStat { @@ -1430,6 +1490,9 @@ abstract class _BowlingStat implements BowlingStat { final double strikeRate, final double economyRate}) = _$BowlingStatImpl; + factory _BowlingStat.fromJson(Map json) = + _$BowlingStatImpl.fromJson; + @override int get innings; @override @@ -1459,12 +1522,19 @@ abstract class _BowlingStat implements BowlingStat { throw _privateConstructorUsedError; } +FieldingStat _$FieldingStatFromJson(Map json) { + return _FieldingStat.fromJson(json); +} + /// @nodoc mixin _$FieldingStat { int get catches => throw _privateConstructorUsedError; int get runOut => throw _privateConstructorUsedError; int get stumping => throw _privateConstructorUsedError; + /// Serializes this FieldingStat to a JSON map. + Map toJson() => throw _privateConstructorUsedError; + /// Create a copy of FieldingStat /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -1563,11 +1633,14 @@ class __$$FieldingStatImplCopyWithImpl<$Res> } /// @nodoc - +@JsonSerializable() class _$FieldingStatImpl implements _FieldingStat { const _$FieldingStatImpl( {this.catches = 0, this.runOut = 0, this.stumping = 0}); + factory _$FieldingStatImpl.fromJson(Map json) => + _$$FieldingStatImplFromJson(json); + @override @JsonKey() final int catches; @@ -1594,6 +1667,7 @@ class _$FieldingStatImpl implements _FieldingStat { other.stumping == stumping)); } + @JsonKey(includeFromJson: false, includeToJson: false) @override int get hashCode => Object.hash(runtimeType, catches, runOut, stumping); @@ -1604,6 +1678,13 @@ class _$FieldingStatImpl implements _FieldingStat { @pragma('vm:prefer-inline') _$$FieldingStatImplCopyWith<_$FieldingStatImpl> get copyWith => __$$FieldingStatImplCopyWithImpl<_$FieldingStatImpl>(this, _$identity); + + @override + Map toJson() { + return _$$FieldingStatImplToJson( + this, + ); + } } abstract class _FieldingStat implements FieldingStat { @@ -1612,6 +1693,9 @@ abstract class _FieldingStat implements FieldingStat { final int runOut, final int stumping}) = _$FieldingStatImpl; + factory _FieldingStat.fromJson(Map json) = + _$FieldingStatImpl.fromJson; + @override int get catches; @override diff --git a/data/lib/api/ball_score/ball_score_model.g.dart b/data/lib/api/ball_score/ball_score_model.g.dart index 912a9272..336b4bcf 100644 --- a/data/lib/api/ball_score/ball_score_model.g.dart +++ b/data/lib/api/ball_score/ball_score_model.g.dart @@ -108,6 +108,96 @@ Json? _$JsonConverterToJson( ) => value == null ? null : toJson(value); +_$UserStatImpl _$$UserStatImplFromJson(Map json) => + _$UserStatImpl( + battingStat: json['battingStat'] == null + ? null + : BattingStat.fromJson(json['battingStat'] as Map), + bowlingStat: json['bowlingStat'] == null + ? null + : BowlingStat.fromJson(json['bowlingStat'] as Map), + fieldingStat: json['fieldingStat'] == null + ? null + : FieldingStat.fromJson(json['fieldingStat'] as Map), + ); + +Map _$$UserStatImplToJson(_$UserStatImpl instance) => + { + 'battingStat': instance.battingStat, + 'bowlingStat': instance.bowlingStat, + 'fieldingStat': instance.fieldingStat, + }; + +_$BattingStatImpl _$$BattingStatImplFromJson(Map json) => + _$BattingStatImpl( + innings: (json['innings'] as num?)?.toInt() ?? 0, + runScored: (json['runScored'] as num?)?.toInt() ?? 0, + average: (json['average'] as num?)?.toDouble() ?? 0.0, + strikeRate: (json['strikeRate'] as num?)?.toDouble() ?? 0.0, + ballFaced: (json['ballFaced'] as num?)?.toInt() ?? 0, + fours: (json['fours'] as num?)?.toInt() ?? 0, + sixes: (json['sixes'] as num?)?.toInt() ?? 0, + fifties: (json['fifties'] as num?)?.toInt() ?? 0, + hundreds: (json['hundreds'] as num?)?.toInt() ?? 0, + ducks: (json['ducks'] as num?)?.toInt() ?? 0, + ); + +Map _$$BattingStatImplToJson(_$BattingStatImpl instance) => + { + 'innings': instance.innings, + 'runScored': instance.runScored, + 'average': instance.average, + 'strikeRate': instance.strikeRate, + 'ballFaced': instance.ballFaced, + 'fours': instance.fours, + 'sixes': instance.sixes, + 'fifties': instance.fifties, + 'hundreds': instance.hundreds, + 'ducks': instance.ducks, + }; + +_$BowlingStatImpl _$$BowlingStatImplFromJson(Map json) => + _$BowlingStatImpl( + innings: (json['innings'] as num?)?.toInt() ?? 0, + wicketTaken: (json['wicketTaken'] as num?)?.toInt() ?? 0, + balls: (json['balls'] as num?)?.toInt() ?? 0, + runsConceded: (json['runsConceded'] as num?)?.toInt() ?? 0, + maiden: (json['maiden'] as num?)?.toInt() ?? 0, + noBalls: (json['noBalls'] as num?)?.toInt() ?? 0, + wideBalls: (json['wideBalls'] as num?)?.toInt() ?? 0, + average: (json['average'] as num?)?.toDouble() ?? 0.0, + strikeRate: (json['strikeRate'] as num?)?.toDouble() ?? 0.0, + economyRate: (json['economyRate'] as num?)?.toDouble() ?? 0.0, + ); + +Map _$$BowlingStatImplToJson(_$BowlingStatImpl instance) => + { + 'innings': instance.innings, + 'wicketTaken': instance.wicketTaken, + 'balls': instance.balls, + 'runsConceded': instance.runsConceded, + 'maiden': instance.maiden, + 'noBalls': instance.noBalls, + 'wideBalls': instance.wideBalls, + 'average': instance.average, + 'strikeRate': instance.strikeRate, + 'economyRate': instance.economyRate, + }; + +_$FieldingStatImpl _$$FieldingStatImplFromJson(Map json) => + _$FieldingStatImpl( + catches: (json['catches'] as num?)?.toInt() ?? 0, + runOut: (json['runOut'] as num?)?.toInt() ?? 0, + stumping: (json['stumping'] as num?)?.toInt() ?? 0, + ); + +Map _$$FieldingStatImplToJson(_$FieldingStatImpl instance) => + { + 'catches': instance.catches, + 'runOut': instance.runOut, + 'stumping': instance.stumping, + }; + _$OverStatModelImpl _$$OverStatModelImplFromJson(Map json) => _$OverStatModelImpl( run: (json['run'] as num?)?.toInt() ?? 0, diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index f7bcb841..4049fc06 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -4,6 +4,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; import '../../converter/timestamp_json_converter.dart'; +import '../ball_score/ball_score_model.dart'; import '../match/match_model.dart'; import '../team/team_model.dart'; import '../user/user_models.dart'; @@ -96,6 +97,84 @@ class PlayerKeyStat with _$PlayerKeyStat { const factory PlayerKeyStat({ required String teamName, required UserModel player, - required int runs, + required UserStat stats, + KeyStatTag? tag, + int? value, }) = _PlayerKeyStat; } + +enum KeyStatTag { + mostRuns, + mostWickets, + mostFours, + mostSixes; +} + +extension PlayerKeyStatListExtensions on List { + List getTopKeyStats() { + final Map playerMap = {}; + + for (final playerStat in this) { + final battingStats = playerStat.stats.battingStat; + final bowlingStats = playerStat.stats.bowlingStat; + + if (bowlingStats == null || battingStats == null) continue; + + int highestRuns = 0; + int highestWickets = 0; + int highestFours = 0; + int highestSixes = 0; + + KeyStatTag? assignedTag; + int highestValue = 0; + + // Check for most runs + if (battingStats.runScored > highestRuns) { + assignedTag = KeyStatTag.mostRuns; + highestRuns = battingStats.runScored; + highestValue = battingStats.runScored; + } + + // Check for most wickets + if (bowlingStats.wicketTaken > highestWickets) { + assignedTag = KeyStatTag.mostWickets; + highestWickets = bowlingStats.wicketTaken; + highestValue = bowlingStats.wicketTaken; + } + + // Check for most fours + if (battingStats.fours > highestFours) { + assignedTag = KeyStatTag.mostFours; + highestFours = battingStats.fours; + highestValue = battingStats.fours; + } + + // Check for most sixes + if (battingStats.sixes > highestSixes) { + assignedTag = KeyStatTag.mostSixes; + highestSixes = battingStats.sixes; + highestValue = battingStats.sixes; + } + + if (assignedTag != null && highestValue > 0) { + if (playerMap.containsKey(playerStat.player.id)) { + final existingPlayerStat = playerMap[playerStat.player.id]!; + + if (highestValue > (existingPlayerStat.value ?? 0)) { + playerMap[playerStat.player.id] = existingPlayerStat.copyWith( + tag: assignedTag, + value: highestValue, + ); + } + } else { + playerMap[playerStat.player.id] = playerStat.copyWith( + tag: assignedTag, + value: highestValue, + ); + } + } + } + + return playerMap.values.toList(); + } +} diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index 2e3bc29e..d0a4858d 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -766,7 +766,9 @@ abstract class _TournamentMember implements TournamentMember { mixin _$PlayerKeyStat { String get teamName => throw _privateConstructorUsedError; UserModel get player => throw _privateConstructorUsedError; - int get runs => throw _privateConstructorUsedError; + UserStat get stats => throw _privateConstructorUsedError; + KeyStatTag? get tag => throw _privateConstructorUsedError; + int? get value => throw _privateConstructorUsedError; /// Create a copy of PlayerKeyStat /// with the given fields replaced by the non-null parameter values. @@ -781,9 +783,15 @@ abstract class $PlayerKeyStatCopyWith<$Res> { PlayerKeyStat value, $Res Function(PlayerKeyStat) then) = _$PlayerKeyStatCopyWithImpl<$Res, PlayerKeyStat>; @useResult - $Res call({String teamName, UserModel player, int runs}); + $Res call( + {String teamName, + UserModel player, + UserStat stats, + KeyStatTag? tag, + int? value}); $UserModelCopyWith<$Res> get player; + $UserStatCopyWith<$Res> get stats; } /// @nodoc @@ -803,7 +811,9 @@ class _$PlayerKeyStatCopyWithImpl<$Res, $Val extends PlayerKeyStat> $Res call({ Object? teamName = null, Object? player = null, - Object? runs = null, + Object? stats = null, + Object? tag = freezed, + Object? value = freezed, }) { return _then(_value.copyWith( teamName: null == teamName @@ -814,10 +824,18 @@ class _$PlayerKeyStatCopyWithImpl<$Res, $Val extends PlayerKeyStat> ? _value.player : player // ignore: cast_nullable_to_non_nullable as UserModel, - runs: null == runs - ? _value.runs - : runs // ignore: cast_nullable_to_non_nullable - as int, + stats: null == stats + ? _value.stats + : stats // ignore: cast_nullable_to_non_nullable + as UserStat, + tag: freezed == tag + ? _value.tag + : tag // ignore: cast_nullable_to_non_nullable + as KeyStatTag?, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as int?, ) as $Val); } @@ -830,6 +848,16 @@ class _$PlayerKeyStatCopyWithImpl<$Res, $Val extends PlayerKeyStat> return _then(_value.copyWith(player: value) as $Val); }); } + + /// Create a copy of PlayerKeyStat + /// with the given fields replaced by the non-null parameter values. + @override + @pragma('vm:prefer-inline') + $UserStatCopyWith<$Res> get stats { + return $UserStatCopyWith<$Res>(_value.stats, (value) { + return _then(_value.copyWith(stats: value) as $Val); + }); + } } /// @nodoc @@ -840,10 +868,17 @@ abstract class _$$PlayerKeyStatImplCopyWith<$Res> __$$PlayerKeyStatImplCopyWithImpl<$Res>; @override @useResult - $Res call({String teamName, UserModel player, int runs}); + $Res call( + {String teamName, + UserModel player, + UserStat stats, + KeyStatTag? tag, + int? value}); @override $UserModelCopyWith<$Res> get player; + @override + $UserStatCopyWith<$Res> get stats; } /// @nodoc @@ -861,7 +896,9 @@ class __$$PlayerKeyStatImplCopyWithImpl<$Res> $Res call({ Object? teamName = null, Object? player = null, - Object? runs = null, + Object? stats = null, + Object? tag = freezed, + Object? value = freezed, }) { return _then(_$PlayerKeyStatImpl( teamName: null == teamName @@ -872,10 +909,18 @@ class __$$PlayerKeyStatImplCopyWithImpl<$Res> ? _value.player : player // ignore: cast_nullable_to_non_nullable as UserModel, - runs: null == runs - ? _value.runs - : runs // ignore: cast_nullable_to_non_nullable - as int, + stats: null == stats + ? _value.stats + : stats // ignore: cast_nullable_to_non_nullable + as UserStat, + tag: freezed == tag + ? _value.tag + : tag // ignore: cast_nullable_to_non_nullable + as KeyStatTag?, + value: freezed == value + ? _value.value + : value // ignore: cast_nullable_to_non_nullable + as int?, )); } } @@ -885,18 +930,26 @@ class __$$PlayerKeyStatImplCopyWithImpl<$Res> @JsonSerializable(explicitToJson: true) class _$PlayerKeyStatImpl implements _PlayerKeyStat { const _$PlayerKeyStatImpl( - {required this.teamName, required this.player, required this.runs}); + {required this.teamName, + required this.player, + required this.stats, + this.tag, + this.value}); @override final String teamName; @override final UserModel player; @override - final int runs; + final UserStat stats; + @override + final KeyStatTag? tag; + @override + final int? value; @override String toString() { - return 'PlayerKeyStat(teamName: $teamName, player: $player, runs: $runs)'; + return 'PlayerKeyStat(teamName: $teamName, player: $player, stats: $stats, tag: $tag, value: $value)'; } @override @@ -907,11 +960,14 @@ class _$PlayerKeyStatImpl implements _PlayerKeyStat { (identical(other.teamName, teamName) || other.teamName == teamName) && (identical(other.player, player) || other.player == player) && - (identical(other.runs, runs) || other.runs == runs)); + (identical(other.stats, stats) || other.stats == stats) && + (identical(other.tag, tag) || other.tag == tag) && + (identical(other.value, value) || other.value == value)); } @override - int get hashCode => Object.hash(runtimeType, teamName, player, runs); + int get hashCode => + Object.hash(runtimeType, teamName, player, stats, tag, value); /// Create a copy of PlayerKeyStat /// with the given fields replaced by the non-null parameter values. @@ -926,14 +982,20 @@ abstract class _PlayerKeyStat implements PlayerKeyStat { const factory _PlayerKeyStat( {required final String teamName, required final UserModel player, - required final int runs}) = _$PlayerKeyStatImpl; + required final UserStat stats, + final KeyStatTag? tag, + final int? value}) = _$PlayerKeyStatImpl; @override String get teamName; @override UserModel get player; @override - int get runs; + UserStat get stats; + @override + KeyStatTag? get tag; + @override + int? get value; /// Create a copy of PlayerKeyStat /// with the given fields replaced by the non-null parameter values. diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index de9022c5..d03d970b 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -102,12 +102,23 @@ _$PlayerKeyStatImpl _$$PlayerKeyStatImplFromJson(Map json) => _$PlayerKeyStatImpl( teamName: json['teamName'] as String, player: UserModel.fromJson(json['player'] as Map), - runs: (json['runs'] as num).toInt(), + stats: UserStat.fromJson(json['stats'] as Map), + tag: $enumDecodeNullable(_$KeyStatTagEnumMap, json['tag']), + value: (json['value'] as num?)?.toInt(), ); Map _$$PlayerKeyStatImplToJson(_$PlayerKeyStatImpl instance) => { 'teamName': instance.teamName, 'player': instance.player.toJson(), - 'runs': instance.runs, + 'stats': instance.stats.toJson(), + 'tag': _$KeyStatTagEnumMap[instance.tag], + 'value': instance.value, }; + +const _$KeyStatTagEnumMap = { + KeyStatTag.mostRuns: 'mostRuns', + KeyStatTag.mostWickets: 'mostWickets', + KeyStatTag.mostFours: 'mostFours', + KeyStatTag.mostSixes: 'mostSixes', +}; diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index deda9c7a..6e733fbf 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -532,18 +532,27 @@ class MatchService { List teamList, ) async { try { - final List teams = []; - - await Future.forEach(teamList, (e) async { - final TeamModel team = await _teamService.getTeamById(e.team_id); - final List squad = - await getPlayerListFromPlayerIds(e.squad); - teams.add( - e.copyWith(team: team, squad: squad), + final teamIds = teamList.map((e) => e.team_id).toList(); + + final teamsData = await Future.wait([ + _teamService.getTeamsByIds(teamIds), + Future.wait( + teamList + .map((team) => getPlayerListFromPlayerIds(team.squad)) + .toList(), + ), + ]); + + final List teams = teamsData[0] as List; + final List> squads = + teamsData[1] as List>; + + return List.generate(teamList.length, (index) { + return teamList[index].copyWith( + team: teams[index], + squad: squads[index], ); }); - - return teams; } catch (error, stack) { throw AppError.fromError(error, stack); } @@ -558,7 +567,7 @@ class MatchService { final List users = await _userService.getUsersByIds(playerIds); - final List squad = users.map((user) { + return users.map((user) { final matchPlayer = players.firstWhere((element) => element.id == user.id); @@ -568,8 +577,6 @@ class MatchService { id: user.id, ); }).toList(); - - return squad; } catch (error, stack) { throw AppError.fromError(error, stack); } @@ -577,13 +584,17 @@ class MatchService { Future> getMatchesByIds(List matchIds) async { try { - final List matches = []; - for (var matchId in matchIds) { - final match = await getMatchById(matchId); - matches.add(match); - } - - return matches; + final snapshot = await _matchCollection + .where(FieldPath.documentId, whereIn: matchIds) + .limit(10) + .get(); + return Future.wait( + snapshot.docs.map((doc) async { + final match = doc.data(); + final List teams = await getTeamsList(match.teams); + return match.copyWith(teams: teams); + }).toList(), + ); } catch (error, stack) { throw AppError.fromError(error, stack); } diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index c252a5dc..cfe0e431 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -1,6 +1,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import '../../api/ball_score/ball_score_model.dart'; import '../../api/match/match_model.dart'; import '../../api/tournament/tournament_model.dart'; import '../../errors/app_error.dart'; @@ -120,32 +121,27 @@ class TournamentService { } Future> getKeyStats(List matches) async { - final Map playerStatsMap = {}; - - for (var match in matches) { - for (var team in match.teams) { - for (var player in team.squad) { - final totalRuns = - await _ballScoreService.getPlayerTotalRuns(match.id, player.id); - - if (totalRuns > 0) { - if (playerStatsMap.containsKey(player.id)) { - playerStatsMap[player.id] = playerStatsMap[player.id]!.copyWith( - runs: playerStatsMap[player.id]!.runs + totalRuns, - ); - } else { - playerStatsMap[player.id] = PlayerKeyStat( - teamName: team.team.name, - player: player.player, - runs: totalRuns, - ); - } - } + final List playerStatsList = []; + final List matchIds = matches.map((match) => match.id).toList(); + final scores = await _ballScoreService.getBallScoresByMatchIds(matchIds); + + for (final match in matches) { + for (final team in match.teams) { + for (final player in team.squad) { + final stats = scores.calculateUserStats(player.id); + + playerStatsList.add( + PlayerKeyStat( + player: player.player, + teamName: team.team.name, + stats: stats, + ), + ); } } } - return playerStatsMap.values.toList() - ..sort((a, b) => b.runs.compareTo(a.runs)); + return playerStatsList.getTopKeyStats() + ..sort((a, b) => b.value?.compareTo(a.value ?? 0) ?? 0); } } diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 3d4aef45..930736ff 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -70,6 +70,8 @@ "common_verify_title": "Verify", "common_verified_title": "Verified", "common_view_all": "View all", + "common_today": "Today", + "common_tomorrow": "Tomorrow", "common_obscure_phone_number_text": "{countryCode} ***** ***{lastDigits}", "@common_obscure_phone_number_text": { "description": "+{countryCode} ***** ***{lastDigits}", @@ -206,13 +208,18 @@ "tournament_detail_stats_tab": "Stats", "tournament_detail_overview_info_title": "Tournament info", + "tournament_detail_overview_name_title": "Tournament Name", "tournament_detail_overview_type_title": "Type", "tournament_detail_overview_duration_title": "Duration", "tournament_detail_overview_teams_squads_title": "Teams Squads", "tournament_detail_overview_key_stats_title": "Key Stats", - "tournament_detail_overview_key_stats_most_runs_title": "Most Runs", "tournament_detail_overview_featured_matches_title": "Featured Matches", + "tournament_detail_key_stat_most_runs_title": "Most Runs", + "tournament_detail_key_stat_most_wickets_title": "Most Wickets", + "tournament_detail_key_stat_most_fours_title": "Most Fours", + "tournament_detail_key_stat_most_sixes_title": "Most Sixes", + "@_TOURNAMENT_TYPE":{ }, "tournament_type_knock_out": "Knockout", @@ -752,6 +759,8 @@ "score_board_w_title": "W", "score_board_o_title": "O", "score_board_won_by_title": " Won By ", + "score_board_won_title": "Won", + "score_board_by_title": "by", "score_board_choose_opening_batsmen_title": "Choose opening Batsmen", "score_board_choose_fielder_title": "Choose fielder", "score_board_injured_tag_title": "Injured", diff --git a/khelo/lib/components/won_by_message_text.dart b/khelo/lib/components/won_by_message_text.dart index 4b6c8586..aa94f46f 100644 --- a/khelo/lib/components/won_by_message_text.dart +++ b/khelo/lib/components/won_by_message_text.dart @@ -34,13 +34,13 @@ class WonByMessageText extends StatelessWidget { return Column( children: [ Text( - '${matchResult!.teamName.initials(limit: 2)} Won', + '${matchResult!.teamName.initials(limit: 2)} ${context.l10n.score_board_won_title}', style: textStyle ?? AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textPrimary), ), Text( - 'by ${matchResult!.winType.getString(context, matchResult!.difference)}', + '${context.l10n.score_board_by_title} ${matchResult!.winType.getString(context, matchResult!.difference)}', style: textStyle ?? AppTextStyle.caption.copyWith( color: context.colorScheme.textDisabled, diff --git a/khelo/lib/domain/extensions/enum_extensions.dart b/khelo/lib/domain/extensions/enum_extensions.dart index 7db17e44..2f2439a1 100644 --- a/khelo/lib/domain/extensions/enum_extensions.dart +++ b/khelo/lib/domain/extensions/enum_extensions.dart @@ -314,3 +314,18 @@ extension TournamentTypeString on TournamentType { } } } + +extension TournamentKeyStatString on KeyStatTag { + String getString(BuildContext context) { + switch (this) { + case KeyStatTag.mostRuns: + return context.l10n.tournament_detail_key_stat_most_runs_title; + case KeyStatTag.mostWickets: + return context.l10n.tournament_detail_key_stat_most_wickets_title; + case KeyStatTag.mostFours: + return context.l10n.tournament_detail_key_stat_most_fours_title; + case KeyStatTag.mostSixes: + return context.l10n.tournament_detail_key_stat_most_sixes_title; + } + } +} diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart index 09019756..bf5d2f69 100644 --- a/khelo/lib/domain/formatter/date_formatter.dart +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -49,9 +49,9 @@ extension DateFormatter on DateTime { String day; if (isSameDay(today)) { - day = 'Today'; + day = context.l10n.common_today; } else if (isSameDay(tomorrow)) { - day = 'Tomorrow'; + day = context.l10n.common_tomorrow; } else if (year == today.year) { day = DateFormat('d MMM').format(this); } else { diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index f1e031af..7adfe7b2 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -35,8 +35,8 @@ class _TournamentDetailOverviewTabState return Container( color: context.colorScheme.containerLow, child: ListView( - padding: context.mediaQueryPadding.copyWith(top: 0) + - EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 40), + padding: EdgeInsets.symmetric(horizontal: 16) + .copyWith(bottom: context.mediaQueryPadding.bottom + 40), children: [ _featuredMatchesView(context, widget.tournament.matches), _keyStatsView(context, widget.tournament.keyStats), @@ -185,6 +185,8 @@ class _TournamentDetailOverviewTabState } Widget _keyStatsCellView(BuildContext context, PlayerKeyStat keyStat) { + if (keyStat.tag == null && keyStat.value == null) return SizedBox(); + return Container( padding: EdgeInsets.all(16), decoration: BoxDecoration( @@ -198,13 +200,12 @@ class _TournamentDetailOverviewTabState mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( - context - .l10n.tournament_detail_overview_key_stats_most_runs_title, + keyStat.tag!.getString(context), style: AppTextStyle.body2 .copyWith(color: context.colorScheme.positive), ), Text( - keyStat.runs.toString(), + keyStat.value.toString(), style: AppTextStyle.subtitle1.copyWith( color: context.colorScheme.textPrimary, ), @@ -331,7 +332,7 @@ class _TournamentDetailOverviewTabState children: [ _infoCellView( context, - context.l10n.tournament_detail_overview_info_title, + context.l10n.tournament_detail_overview_name_title, tournament.name, ), Divider(color: context.colorScheme.outline, height: 1), @@ -369,13 +370,14 @@ class _TournamentDetailOverviewTabState style: AppTextStyle.subtitle3 .copyWith(color: context.colorScheme.textSecondary), ), - const SizedBox(width: 8), + const SizedBox(width: 16), Expanded( child: Text( value, style: AppTextStyle.subtitle2 .copyWith(color: context.colorScheme.textPrimary), textAlign: TextAlign.end, + textScaler: TextScaler.noScaling, ), ), ], diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index 307d26c8..731ce6a6 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -82,34 +82,35 @@ class _TournamentDetailScreenState ); } - return CustomScrollView( - slivers: [ - SliverAppBar( - pinned: true, - expandedHeight: 300, - backgroundColor: context.colorScheme.surface, - flexibleSpace: _flexibleTitle(context, state.tournament!), - actions: [ - moreOptionButton(context), - ], - ), - SliverPersistentHeader( - pinned: true, - delegate: SliverPersistentDelegate( - child: _tabSelection(context), - size: 70, + return NestedScrollView( + headerSliverBuilder: (context, innerBoxIsScrolled) { + return [ + SliverAppBar( + pinned: true, + expandedHeight: 300, + backgroundColor: context.colorScheme.surface, + flexibleSpace: _flexibleTitle(context, state.tournament!), + actions: [ + moreOptionButton(context), + ], ), - ), - SliverFillRemaining( - child: _content(context, state), - ), - ], + SliverPersistentHeader( + pinned: true, + delegate: SliverPersistentDelegate( + child: _tabSelection(context), + size: 70, + ), + ), + ]; + }, + body: _content(context, state), ); } Widget _content(BuildContext context, TournamentDetailState state) { return PageView( controller: _controller, + physics: const NeverScrollableScrollPhysics(), onPageChanged: notifier.onTabChange, children: [ TournamentDetailOverviewTab( @@ -155,14 +156,20 @@ class _TournamentDetailScreenState final isCollapsed = constraints.biggest.height < 150; return FlexibleSpaceBar( + centerTitle: true, title: isCollapsed ? AnimatedOpacity( opacity: isCollapsed ? 1 : 0, duration: const Duration(milliseconds: 100), - child: Text( - tournament.name, - style: AppTextStyle.header2.copyWith( - color: context.colorScheme.textPrimary, + child: Padding( + padding: const EdgeInsets.only(left: 64, right: 48), + child: Text( + tournament.name, + style: AppTextStyle.header2.copyWith( + color: context.colorScheme.textPrimary, + ), + overflow: TextOverflow.ellipsis, + textScaler: TextScaler.noScaling, ), ), ) @@ -222,9 +229,15 @@ class _TournamentDetailScreenState backgroundColor: context.colorScheme.primary, ), const SizedBox(height: 16), - Text( - tournament.name, - style: AppTextStyle.header1.copyWith(color: Colors.white), + SizedBox( + width: context.mediaQuerySize.width - 32, + child: Text( + tournament.name, + style: AppTextStyle.header1.copyWith(color: Colors.white), + overflow: TextOverflow.ellipsis, + textScaler: TextScaler.noScaling, + maxLines: 2, + ), ), const SizedBox(height: 4), Row( diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index b9adfada..5f9bd3a5 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -145,6 +145,8 @@ class _TournamentListScreenState extends ConsumerState style: AppTextStyle.header4.copyWith( color: context.colorScheme.textPrimary, ), + overflow: TextOverflow.ellipsis, + maxLines: 2, ), const SizedBox(height: 4), Text( From 33745ac6bc91cce6ab1f4dd1886be899238c3595 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Mon, 21 Oct 2024 18:23:31 +0530 Subject: [PATCH 26/33] Mr changes --- data/.flutter-plugins-dependencies | 2 +- .../ball_score/ball_score_service.dart | 21 ------- data/lib/service/match/match_service.dart | 7 ++- .../provider/preferences_provider.dart | 63 ++++++++++++++----- data/pubspec.yaml | 4 +- khelo/lib/main.dart | 3 +- .../tabs/tournament_detail_overview_tab.dart | 20 +++--- khelo/pubspec.lock | 12 ++-- khelo/pubspec.yaml | 4 +- 9 files changed, 76 insertions(+), 60 deletions(-) diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index ba746dec..c88625f5 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.3.2/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.2/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.2/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.1/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.2/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.2/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-10-07 16:21:53.256543","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.3.2/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.2/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.2/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.1/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.2/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.2/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-10-21 16:09:49.595358","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/service/ball_score/ball_score_service.dart b/data/lib/service/ball_score/ball_score_service.dart index 3a754e90..144920ea 100644 --- a/data/lib/service/ball_score/ball_score_service.dart +++ b/data/lib/service/ball_score/ball_score_service.dart @@ -182,27 +182,6 @@ class BallScoreService { throw AppError.fromError(error, stack); } } - - Future getPlayerTotalRuns(String matchId, String playerId) async { - try { - final filter = Filter.and( - Filter(FireStoreConst.matchId, isEqualTo: matchId), - Filter(FireStoreConst.batsmanId, isEqualTo: playerId), - ); - - final snapshot = await _ballScoreCollection.where(filter).get(); - final totalRuns = snapshot.docs.fold( - 0, - (total, doc) { - final runsScored = doc.data().runs_scored; - return runsScored > 0 ? total + runsScored : total; - }, - ); - return totalRuns; - } catch (error, stack) { - throw AppError.fromError(error, stack); - } - } } class BallScoreChange { diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index 6e733fbf..d5da656b 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -584,9 +584,14 @@ class MatchService { Future> getMatchesByIds(List matchIds) async { try { + if (matchIds.length > 10) { + throw AppError.fromError( + "You can only query up to 10 matches at a time.", + ); + } + final snapshot = await _matchCollection .where(FieldPath.documentId, whereIn: matchIds) - .limit(10) .get(); return Future.wait( snapshot.docs.map((doc) async { diff --git a/data/lib/storage/provider/preferences_provider.dart b/data/lib/storage/provider/preferences_provider.dart index 350b02be..8591b398 100644 --- a/data/lib/storage/provider/preferences_provider.dart +++ b/data/lib/storage/provider/preferences_provider.dart @@ -1,31 +1,62 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; +class MockSharedPreferencesNotifier extends SharedPreferencesNotifier { + final SharedPreferences mockPrefs; + + MockSharedPreferencesNotifier(this.mockPrefs); + + @override + SharedPreferences build() => mockPrefs; +} + +class SharedPreferencesNotifier extends Notifier { + @override + SharedPreferences build() { + throw UnimplementedError(); + } + + Future init() async { + final prefs = await SharedPreferences.getInstance(); + state = prefs; + } +} + final sharedPreferencesProvider = - Provider((ref) => throw UnimplementedError()); + NotifierProvider( + SharedPreferencesNotifier.new, +); StateProvider createPrefProvider({ required String prefKey, required T defaultValue, }) { - return StateProvider((ref) { + return StateProvider((ref) { final prefs = ref.watch(sharedPreferencesProvider); + final notifier = ref.read(sharedPreferencesProvider.notifier); + final currentValue = prefs.get(prefKey) as T? ?? defaultValue; - ref.listenSelf((prev, curr) { - if (curr == null) { - prefs.remove(prefKey); - } else if (curr is String) { - prefs.setString(prefKey, curr); - } else if (curr is bool) { - prefs.setBool(prefKey, curr); - } else if (curr is int) { - prefs.setInt(prefKey, curr); - } else if (curr is double) { - prefs.setDouble(prefKey, curr); - } else if (curr is List) { - prefs.setStringList(prefKey, curr); - } + + notifier.listenSelf((previous, next) { + _saveToPreferences(prefs, prefKey, next); }); + return currentValue; }); } + +void _saveToPreferences(SharedPreferences prefs, String key, T value) { + if (value == null) { + prefs.remove(key); + } else if (value is String) { + prefs.setString(key, value); + } else if (value is bool) { + prefs.setBool(key, value); + } else if (value is int) { + prefs.setInt(key, value); + } else if (value is double) { + prefs.setDouble(key, value); + } else if (value is List) { + prefs.setStringList(key, value); + } +} diff --git a/data/pubspec.yaml b/data/pubspec.yaml index c4ff5cc4..014d9781 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -40,8 +40,8 @@ dependencies: uuid: ^4.5.1 # state management - flutter_riverpod: ^2.5.1 - hooks_riverpod: ^2.5.2 + flutter_riverpod: ^2.6.0 + hooks_riverpod: ^2.6.0 dev_dependencies: flutter_test: diff --git a/khelo/lib/main.dart b/khelo/lib/main.dart index 2c097d95..84b87871 100644 --- a/khelo/lib/main.dart +++ b/khelo/lib/main.dart @@ -44,7 +44,8 @@ Future _initContainer() async { final container = ProviderContainer( overrides: [ - sharedPreferencesProvider.overrideWithValue(prefs), + sharedPreferencesProvider + .overrideWith(() => MockSharedPreferencesNotifier(prefs)), ], ); return container; diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index 7adfe7b2..4b0c86fd 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -82,14 +82,14 @@ class _TournamentDetailOverviewTabState children: [ _buildTeamInfo(team: match.teams.first.team), const Spacer(), - Column( - children: [ - if (match.matchResult != null) ...[ - WonByMessageText( - isTournament: true, - matchResult: match.matchResult, - ), - ] else ...[ + if (match.matchResult != null) ...[ + WonByMessageText( + isTournament: true, + matchResult: match.matchResult, + ), + ] else ...[ + Column( + children: [ Text( match.start_at?.format(context, DateFormatType.time) ?? DateTime.now().format(context, DateFormatType.time), @@ -103,8 +103,8 @@ class _TournamentDetailOverviewTabState .copyWith(color: context.colorScheme.textPrimary), ), ], - ], - ), + ), + ], const Spacer(), _buildTeamInfo(team: match.teams.last.team, isSecond: true), ], diff --git a/khelo/pubspec.lock b/khelo/pubspec.lock index 18881291..4ae752bf 100644 --- a/khelo/pubspec.lock +++ b/khelo/pubspec.lock @@ -675,10 +675,10 @@ packages: dependency: "direct main" description: name: flutter_riverpod - sha256: "0f1974eff5bbe774bf1d870e406fc6f29e3d6f1c46bd9c58e7172ff68a785d7d" + sha256: "6eda4e247774474c715a0805a2fb8e3cd55fbae4ead641e063c95b4bd5f3b317" url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.6.0" flutter_svg: dependency: "direct main" description: @@ -781,10 +781,10 @@ packages: dependency: "direct main" description: name: hooks_riverpod - sha256: "97266a91c994951a06ef0ff3a1c7fb261e52ec7f74e87f0614ea0b7411b859b2" + sha256: "8010a5abaa625a98358d9bce14b56f63dacdb4ec8637544da3bfd0ae184a9ae6" url: "https://pub.dev" source: hosted - version: "2.5.2" + version: "2.6.0" hotreloader: dependency: transitive description: @@ -1261,10 +1261,10 @@ packages: dependency: transitive description: name: riverpod - sha256: f21b32ffd26a36555e501b04f4a5dca43ed59e16343f1a30c13632b2351dfa4d + sha256: bd6e656a764e3d27f211975626e0c4f9b8d06ab16acf3c7ba7a8061e09744c75 url: "https://pub.dev" source: hosted - version: "2.5.1" + version: "2.6.0" riverpod_analyzer_utils: dependency: transitive description: diff --git a/khelo/pubspec.yaml b/khelo/pubspec.yaml index 8581828a..7eeecf9e 100644 --- a/khelo/pubspec.yaml +++ b/khelo/pubspec.yaml @@ -75,8 +75,8 @@ dependencies: firebase_messaging: ^15.1.3 # state management - flutter_riverpod: ^2.5.1 - hooks_riverpod: ^2.5.1 + flutter_riverpod: ^2.6.0 + hooks_riverpod: ^2.6.0 # navigation go_router: ^14.3.0 From 9fc95f09448fee8f16e83599cd86c24473540b71 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Mon, 21 Oct 2024 19:02:52 +0530 Subject: [PATCH 27/33] Minor changes --- data/lib/api/tournament/tournament_model.dart | 2 +- .../tournament/tournament_model.freezed.dart | 26 +++++++++---------- .../api/tournament/tournament_model.g.dart | 7 +++-- .../tournament/tournament_service.dart | 1 + .../lib/domain/formatter/date_formatter.dart | 5 ++-- .../tabs/tournament_detail_overview_tab.dart | 4 +-- .../detail/tournament_detail_screen.dart | 20 ++++++++++---- 7 files changed, 37 insertions(+), 28 deletions(-) diff --git a/data/lib/api/tournament/tournament_model.dart b/data/lib/api/tournament/tournament_model.dart index 4049fc06..0b6ee4a5 100644 --- a/data/lib/api/tournament/tournament_model.dart +++ b/data/lib/api/tournament/tournament_model.dart @@ -26,7 +26,7 @@ class TournamentModel with _$TournamentModel { required String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() required DateTime start_date, - @TimeStampJsonConverter() DateTime? end_date, + @TimeStampJsonConverter() required DateTime end_date, @Default([]) List team_ids, @Default([]) List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) diff --git a/data/lib/api/tournament/tournament_model.freezed.dart b/data/lib/api/tournament/tournament_model.freezed.dart index d0a4858d..75028f1b 100644 --- a/data/lib/api/tournament/tournament_model.freezed.dart +++ b/data/lib/api/tournament/tournament_model.freezed.dart @@ -32,7 +32,7 @@ mixin _$TournamentModel { @TimeStampJsonConverter() DateTime get start_date => throw _privateConstructorUsedError; @TimeStampJsonConverter() - DateTime? get end_date => throw _privateConstructorUsedError; + DateTime get end_date => throw _privateConstructorUsedError; List get team_ids => throw _privateConstructorUsedError; List get match_ids => throw _privateConstructorUsedError; @JsonKey(includeFromJson: false, includeToJson: false) @@ -68,7 +68,7 @@ abstract class $TournamentModelCopyWith<$Res> { String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() DateTime start_date, - @TimeStampJsonConverter() DateTime? end_date, + @TimeStampJsonConverter() DateTime end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -103,7 +103,7 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> Object? created_by = null, Object? created_at = freezed, Object? start_date = null, - Object? end_date = freezed, + Object? end_date = null, Object? team_ids = null, Object? match_ids = null, Object? teams = null, @@ -147,10 +147,10 @@ class _$TournamentModelCopyWithImpl<$Res, $Val extends TournamentModel> ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable as DateTime, - end_date: freezed == end_date + end_date: null == end_date ? _value.end_date : end_date // ignore: cast_nullable_to_non_nullable - as DateTime?, + as DateTime, team_ids: null == team_ids ? _value.team_ids : team_ids // ignore: cast_nullable_to_non_nullable @@ -193,7 +193,7 @@ abstract class _$$TournamentModelImplCopyWith<$Res> String created_by, @TimeStampJsonConverter() DateTime? created_at, @TimeStampJsonConverter() DateTime start_date, - @TimeStampJsonConverter() DateTime? end_date, + @TimeStampJsonConverter() DateTime end_date, List team_ids, List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -226,7 +226,7 @@ class __$$TournamentModelImplCopyWithImpl<$Res> Object? created_by = null, Object? created_at = freezed, Object? start_date = null, - Object? end_date = freezed, + Object? end_date = null, Object? team_ids = null, Object? match_ids = null, Object? teams = null, @@ -270,10 +270,10 @@ class __$$TournamentModelImplCopyWithImpl<$Res> ? _value.start_date : start_date // ignore: cast_nullable_to_non_nullable as DateTime, - end_date: freezed == end_date + end_date: null == end_date ? _value.end_date : end_date // ignore: cast_nullable_to_non_nullable - as DateTime?, + as DateTime, team_ids: null == team_ids ? _value._team_ids : team_ids // ignore: cast_nullable_to_non_nullable @@ -312,7 +312,7 @@ class _$TournamentModelImpl implements _TournamentModel { required this.created_by, @TimeStampJsonConverter() this.created_at, @TimeStampJsonConverter() required this.start_date, - @TimeStampJsonConverter() this.end_date, + @TimeStampJsonConverter() required this.end_date, final List team_ids = const [], final List match_ids = const [], @JsonKey(includeFromJson: false, includeToJson: false) @@ -360,7 +360,7 @@ class _$TournamentModelImpl implements _TournamentModel { final DateTime start_date; @override @TimeStampJsonConverter() - final DateTime? end_date; + final DateTime end_date; final List _team_ids; @override @JsonKey() @@ -488,7 +488,7 @@ abstract class _TournamentModel implements TournamentModel { required final String created_by, @TimeStampJsonConverter() final DateTime? created_at, @TimeStampJsonConverter() required final DateTime start_date, - @TimeStampJsonConverter() final DateTime? end_date, + @TimeStampJsonConverter() required final DateTime end_date, final List team_ids, final List match_ids, @JsonKey(includeFromJson: false, includeToJson: false) @@ -523,7 +523,7 @@ abstract class _TournamentModel implements TournamentModel { DateTime get start_date; @override @TimeStampJsonConverter() - DateTime? get end_date; + DateTime get end_date; @override List get team_ids; @override diff --git a/data/lib/api/tournament/tournament_model.g.dart b/data/lib/api/tournament/tournament_model.g.dart index d03d970b..4085349e 100644 --- a/data/lib/api/tournament/tournament_model.g.dart +++ b/data/lib/api/tournament/tournament_model.g.dart @@ -23,8 +23,8 @@ _$TournamentModelImpl _$$TournamentModelImplFromJson(Map json) => json['created_at'], const TimeStampJsonConverter().fromJson), start_date: const TimeStampJsonConverter().fromJson(json['start_date'] as Object), - end_date: _$JsonConverterFromJson( - json['end_date'], const TimeStampJsonConverter().fromJson), + end_date: + const TimeStampJsonConverter().fromJson(json['end_date'] as Object), team_ids: (json['team_ids'] as List?) ?.map((e) => e as String) .toList() ?? @@ -48,8 +48,7 @@ Map _$$TournamentModelImplToJson( 'created_at': _$JsonConverterToJson( instance.created_at, const TimeStampJsonConverter().toJson), 'start_date': const TimeStampJsonConverter().toJson(instance.start_date), - 'end_date': _$JsonConverterToJson( - instance.end_date, const TimeStampJsonConverter().toJson), + 'end_date': const TimeStampJsonConverter().toJson(instance.end_date), 'team_ids': instance.team_ids, 'match_ids': instance.match_ids, }; diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index cfe0e431..a4612be2 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -85,6 +85,7 @@ class TournamentService { created_by: '', type: TournamentType.other, start_date: DateTime.now(), + end_date: DateTime.now().add(const Duration(days: 1)), ); } else { var tournament = snapshot.data()!; diff --git a/khelo/lib/domain/formatter/date_formatter.dart b/khelo/lib/domain/formatter/date_formatter.dart index bf5d2f69..cf64b303 100644 --- a/khelo/lib/domain/formatter/date_formatter.dart +++ b/khelo/lib/domain/formatter/date_formatter.dart @@ -64,9 +64,8 @@ extension DateFormatter on DateTime { static String formatDateRange( BuildContext context, { required DateTime startDate, - DateTime? endDate, + required DateTime endDate, required DateFormatType formatType, }) => - endDate?.format(context, formatType) ?? - "${startDate.format(context, formatType)} - ${endDate!.format(context, formatType)}"; + "${startDate.format(context, formatType)} - ${endDate.format(context, formatType)}"; } diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index 4b0c86fd..b1ee7eff 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -35,8 +35,8 @@ class _TournamentDetailOverviewTabState return Container( color: context.colorScheme.containerLow, child: ListView( - padding: EdgeInsets.symmetric(horizontal: 16) - .copyWith(bottom: context.mediaQueryPadding.bottom + 40), + padding: context.mediaQueryPadding.copyWith(top: 0) + + EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 40), children: [ _featuredMatchesView(context, widget.tournament.matches), _keyStatsView(context, widget.tournament.keyStats), diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index 731ce6a6..a9616334 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -178,7 +178,7 @@ class _TournamentDetailScreenState children: [ Container( decoration: BoxDecoration( - color: context.colorScheme.containerLow, + color: context.colorScheme.containerHigh, image: (tournament.banner_img_url != null) ? DecorationImage( image: CachedNetworkImageProvider( @@ -192,7 +192,7 @@ class _TournamentDetailScreenState child: SvgPicture.asset( Assets.images.icTournaments, colorFilter: ColorFilter.mode( - context.colorScheme.textPrimary, + context.colorScheme.textSecondary, BlendMode.srcIn, ), ), @@ -233,7 +233,11 @@ class _TournamentDetailScreenState width: context.mediaQuerySize.width - 32, child: Text( tournament.name, - style: AppTextStyle.header1.copyWith(color: Colors.white), + style: AppTextStyle.header1.copyWith( + color: tournament.banner_img_url != null + ? Colors.white + : context.colorScheme.textPrimary, + ), overflow: TextOverflow.ellipsis, textScaler: TextScaler.noScaling, maxLines: 2, @@ -247,7 +251,9 @@ class _TournamentDetailScreenState height: 24, width: 24, colorFilter: ColorFilter.mode( - Colors.white, + tournament.banner_img_url != null + ? Colors.white + : context.colorScheme.textPrimary, BlendMode.srcIn, ), ), @@ -256,7 +262,11 @@ class _TournamentDetailScreenState context.l10n.tournament_detail_start_from_title(tournament .start_date .format(context, DateFormatType.dayMonth)), - style: AppTextStyle.body1.copyWith(color: Colors.white), + style: AppTextStyle.body1.copyWith( + color: tournament.banner_img_url != null + ? Colors.white + : context.colorScheme.textPrimary, + ), ), ], ) From 054ba0506cd7a3c434186acda7552928ca004a20 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Mon, 21 Oct 2024 19:05:56 +0530 Subject: [PATCH 28/33] Minor changes --- khelo/lib/ui/flow/tournament/tournament_list_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart index 5f9bd3a5..2ec8a825 100644 --- a/khelo/lib/ui/flow/tournament/tournament_list_screen.dart +++ b/khelo/lib/ui/flow/tournament/tournament_list_screen.dart @@ -208,7 +208,7 @@ class _TournamentListScreenState extends ConsumerState text: DateFormatter.formatDateRange( context, startDate: tournament.start_date, - endDate: tournament.end_date!, + endDate: tournament.end_date, formatType: DateFormatType.dayMonth, ), style: AppTextStyle.caption From f1dc02fcd8e498ef4c61af6f95c3148d0598f679 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Tue, 22 Oct 2024 11:54:57 +0530 Subject: [PATCH 29/33] Minor changes --- data/.flutter-plugins-dependencies | 2 +- .../provider/preferences_provider.dart | 63 +++++-------------- data/pubspec.yaml | 4 +- khelo/lib/main.dart | 3 +- khelo/pubspec.lock | 12 ++-- khelo/pubspec.yaml | 4 +- 6 files changed, 28 insertions(+), 60 deletions(-) diff --git a/data/.flutter-plugins-dependencies b/data/.flutter-plugins-dependencies index c88625f5..49161e34 100644 --- a/data/.flutter-plugins-dependencies +++ b/data/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.3.2/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.2/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.2/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.1/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.2/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.2/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-10-21 16:09:49.595358","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_android-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"cloud_functions","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions-5.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":true,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_messaging","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging-15.1.3/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.5.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"linux":[{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"shared_preferences_linux","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.4.1/","native_build":false,"dependencies":["path_provider_linux"]}],"windows":[{"name":"cloud_firestore","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore-5.4.4/","native_build":true,"dependencies":["firebase_core"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","native_build":false,"dependencies":[]},{"name":"firebase_auth","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth-5.3.1/","native_build":true,"dependencies":["firebase_core"]},{"name":"firebase_core","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core-3.6.0/","native_build":true,"dependencies":[]},{"name":"firebase_storage","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage-12.3.2/","native_build":true,"dependencies":["firebase_core"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","native_build":true,"dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","native_build":false,"dependencies":[]},{"name":"path_provider_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/path_provider_windows-2.3.0/","native_build":false,"dependencies":[]},{"name":"shared_preferences_windows","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.4.1/","native_build":false,"dependencies":["path_provider_windows"]}],"web":[{"name":"cloud_firestore_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_firestore_web-4.3.2/","dependencies":["firebase_core_web"]},{"name":"cloud_functions_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/cloud_functions_web-4.10.2/","dependencies":["firebase_core_web"]},{"name":"device_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/device_info_plus-10.1.2/","dependencies":[]},{"name":"firebase_auth_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_auth_web-5.13.2/","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_core_web-2.18.1/","dependencies":[]},{"name":"firebase_messaging_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_messaging_web-3.9.2/","dependencies":["firebase_core_web"]},{"name":"firebase_storage_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/firebase_storage_web-3.10.2/","dependencies":["firebase_core_web"]},{"name":"flutter_timezone","path":"/home/mayank/.pub-cache/hosted/pub.dev/flutter_timezone-3.0.1/","dependencies":[]},{"name":"package_info_plus","path":"/home/mayank/.pub-cache/hosted/pub.dev/package_info_plus-8.0.2/","dependencies":[]},{"name":"shared_preferences_web","path":"/home/mayank/.pub-cache/hosted/pub.dev/shared_preferences_web-2.4.2/","dependencies":[]}]},"dependencyGraph":[{"name":"cloud_firestore","dependencies":["cloud_firestore_web","firebase_core"]},{"name":"cloud_firestore_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"cloud_functions","dependencies":["cloud_functions_web","firebase_core"]},{"name":"cloud_functions_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"device_info_plus","dependencies":[]},{"name":"firebase_auth","dependencies":["firebase_auth_web","firebase_core"]},{"name":"firebase_auth_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_core","dependencies":["firebase_core_web"]},{"name":"firebase_core_web","dependencies":[]},{"name":"firebase_messaging","dependencies":["firebase_core","firebase_messaging_web"]},{"name":"firebase_messaging_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"firebase_storage","dependencies":["firebase_core","firebase_storage_web"]},{"name":"firebase_storage_web","dependencies":["firebase_core","firebase_core_web"]},{"name":"flutter_timezone","dependencies":[]},{"name":"package_info_plus","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]}],"date_created":"2024-10-22 11:52:42.956224","version":"3.24.3","swift_package_manager_enabled":false} \ No newline at end of file diff --git a/data/lib/storage/provider/preferences_provider.dart b/data/lib/storage/provider/preferences_provider.dart index 8591b398..350b02be 100644 --- a/data/lib/storage/provider/preferences_provider.dart +++ b/data/lib/storage/provider/preferences_provider.dart @@ -1,62 +1,31 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:shared_preferences/shared_preferences.dart'; -class MockSharedPreferencesNotifier extends SharedPreferencesNotifier { - final SharedPreferences mockPrefs; - - MockSharedPreferencesNotifier(this.mockPrefs); - - @override - SharedPreferences build() => mockPrefs; -} - -class SharedPreferencesNotifier extends Notifier { - @override - SharedPreferences build() { - throw UnimplementedError(); - } - - Future init() async { - final prefs = await SharedPreferences.getInstance(); - state = prefs; - } -} - final sharedPreferencesProvider = - NotifierProvider( - SharedPreferencesNotifier.new, -); + Provider((ref) => throw UnimplementedError()); StateProvider createPrefProvider({ required String prefKey, required T defaultValue, }) { - return StateProvider((ref) { + return StateProvider((ref) { final prefs = ref.watch(sharedPreferencesProvider); - final notifier = ref.read(sharedPreferencesProvider.notifier); - final currentValue = prefs.get(prefKey) as T? ?? defaultValue; - - notifier.listenSelf((previous, next) { - _saveToPreferences(prefs, prefKey, next); + ref.listenSelf((prev, curr) { + if (curr == null) { + prefs.remove(prefKey); + } else if (curr is String) { + prefs.setString(prefKey, curr); + } else if (curr is bool) { + prefs.setBool(prefKey, curr); + } else if (curr is int) { + prefs.setInt(prefKey, curr); + } else if (curr is double) { + prefs.setDouble(prefKey, curr); + } else if (curr is List) { + prefs.setStringList(prefKey, curr); + } }); - return currentValue; }); } - -void _saveToPreferences(SharedPreferences prefs, String key, T value) { - if (value == null) { - prefs.remove(key); - } else if (value is String) { - prefs.setString(key, value); - } else if (value is bool) { - prefs.setBool(key, value); - } else if (value is int) { - prefs.setInt(key, value); - } else if (value is double) { - prefs.setDouble(key, value); - } else if (value is List) { - prefs.setStringList(key, value); - } -} diff --git a/data/pubspec.yaml b/data/pubspec.yaml index 014d9781..75123484 100644 --- a/data/pubspec.yaml +++ b/data/pubspec.yaml @@ -40,8 +40,8 @@ dependencies: uuid: ^4.5.1 # state management - flutter_riverpod: ^2.6.0 - hooks_riverpod: ^2.6.0 + flutter_riverpod: 2.5.3 + hooks_riverpod: ^2.5.4 dev_dependencies: flutter_test: diff --git a/khelo/lib/main.dart b/khelo/lib/main.dart index 84b87871..2c097d95 100644 --- a/khelo/lib/main.dart +++ b/khelo/lib/main.dart @@ -44,8 +44,7 @@ Future _initContainer() async { final container = ProviderContainer( overrides: [ - sharedPreferencesProvider - .overrideWith(() => MockSharedPreferencesNotifier(prefs)), + sharedPreferencesProvider.overrideWithValue(prefs), ], ); return container; diff --git a/khelo/pubspec.lock b/khelo/pubspec.lock index 4ae752bf..3e92fc2e 100644 --- a/khelo/pubspec.lock +++ b/khelo/pubspec.lock @@ -675,10 +675,10 @@ packages: dependency: "direct main" description: name: flutter_riverpod - sha256: "6eda4e247774474c715a0805a2fb8e3cd55fbae4ead641e063c95b4bd5f3b317" + sha256: "711d916456563f715bde1e139d7cfdca009f8264befab3ac9f8ded8b6ec26405" url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.5.3" flutter_svg: dependency: "direct main" description: @@ -781,10 +781,10 @@ packages: dependency: "direct main" description: name: hooks_riverpod - sha256: "8010a5abaa625a98358d9bce14b56f63dacdb4ec8637544da3bfd0ae184a9ae6" + sha256: "534ca8af331235f700f3916a74e4579cdfdd708e2fbed342fc8aa969d41a7af0" url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.5.4" hotreloader: dependency: transitive description: @@ -1261,10 +1261,10 @@ packages: dependency: transitive description: name: riverpod - sha256: bd6e656a764e3d27f211975626e0c4f9b8d06ab16acf3c7ba7a8061e09744c75 + sha256: c86fedfb45dd1da98ee6493dd9374325cdf494e7d523ebfb0c387eecc5f7b5c9 url: "https://pub.dev" source: hosted - version: "2.6.0" + version: "2.5.3" riverpod_analyzer_utils: dependency: transitive description: diff --git a/khelo/pubspec.yaml b/khelo/pubspec.yaml index 7eeecf9e..5662069f 100644 --- a/khelo/pubspec.yaml +++ b/khelo/pubspec.yaml @@ -75,8 +75,8 @@ dependencies: firebase_messaging: ^15.1.3 # state management - flutter_riverpod: ^2.6.0 - hooks_riverpod: ^2.6.0 + flutter_riverpod: 2.5.3 + hooks_riverpod: 2.5.4 # navigation go_router: ^14.3.0 From 4f76745945b694885c23a9f257bdf2b1820ff7ca Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 23 Oct 2024 11:37:01 +0530 Subject: [PATCH 30/33] Minor changes --- data/lib/service/match/match_service.dart | 43 ++++++++++++----------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index d5da656b..ac503de3 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -533,19 +533,19 @@ class MatchService { ) async { try { final teamIds = teamList.map((e) => e.team_id).toList(); + final playerIds = teamList.expand((team) => team.squad).toList(); final teamsData = await Future.wait([ _teamService.getTeamsByIds(teamIds), - Future.wait( - teamList - .map((team) => getPlayerListFromPlayerIds(team.squad)) - .toList(), - ), + getPlayerListFromPlayerIds(playerIds), ]); final List teams = teamsData[0] as List; - final List> squads = - teamsData[1] as List>; + final List players = teamsData[1] as List; + + final List> squads = teamList.map((team) { + return players.where((player) => team.squad.contains(player)).toList(); + }).toList(); return List.generate(teamList.length, (index) { return teamList[index].copyWith( @@ -583,23 +583,24 @@ class MatchService { } Future> getMatchesByIds(List matchIds) async { + final List matches = []; try { - if (matchIds.length > 10) { - throw AppError.fromError( - "You can only query up to 10 matches at a time.", + if (matchIds.isEmpty) return []; + for (final tenIds in matchIds.chunked(10)) { + final snapshot = await _matchCollection + .where(FieldPath.documentId, whereIn: tenIds) + .get(); + final matchList = await Future.wait( + snapshot.docs.map((doc) async { + final match = doc.data(); + final List teams = await getTeamsList(match.teams); + return match.copyWith(teams: teams); + }).toList(), ); - } - final snapshot = await _matchCollection - .where(FieldPath.documentId, whereIn: matchIds) - .get(); - return Future.wait( - snapshot.docs.map((doc) async { - final match = doc.data(); - final List teams = await getTeamsList(match.teams); - return match.copyWith(teams: teams); - }).toList(), - ); + matches.addAll(matchList); + } + return matches; } catch (error, stack) { throw AppError.fromError(error, stack); } From 84cbfb15ea1418d8d291bda438cae51bf9b945cc Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 23 Oct 2024 14:37:17 +0530 Subject: [PATCH 31/33] Implement tournament teams tab --- data/lib/service/match/match_service.dart | 10 +- .../tournament/tournament_service.dart | 10 ++ khelo/assets/locales/app_en.arb | 4 + .../tabs/tournament_detail_overview_tab.dart | 23 ++-- .../tabs/tournament_detail_teams_tab.dart | 112 ++++++++++++++++++ .../detail/tournament_detail_screen.dart | 40 +++++-- .../detail/tournament_detail_view_model.dart | 14 +++ .../tournament_detail_view_model.freezed.dart | 36 ++++-- 8 files changed, 217 insertions(+), 32 deletions(-) create mode 100644 khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index ac503de3..57e9e1c4 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -533,7 +533,10 @@ class MatchService { ) async { try { final teamIds = teamList.map((e) => e.team_id).toList(); - final playerIds = teamList.expand((team) => team.squad).toList(); + + final playerIds = teamList + .expand((team) => team.squad.map((player) => player)) + .toList(); final teamsData = await Future.wait([ _teamService.getTeamsByIds(teamIds), @@ -544,7 +547,10 @@ class MatchService { final List players = teamsData[1] as List; final List> squads = teamList.map((team) { - return players.where((player) => team.squad.contains(player)).toList(); + final teamPlayerIds = team.squad.map((player) => player.id).toSet(); + return players + .where((player) => teamPlayerIds.contains(player.id)) + .toList(); }).toList(); return List.generate(teamList.length, (index) { diff --git a/data/lib/service/tournament/tournament_service.dart b/data/lib/service/tournament/tournament_service.dart index a4612be2..1d54d0ad 100644 --- a/data/lib/service/tournament/tournament_service.dart +++ b/data/lib/service/tournament/tournament_service.dart @@ -145,4 +145,14 @@ class TournamentService { return playerStatsList.getTopKeyStats() ..sort((a, b) => b.value?.compareTo(a.value ?? 0) ?? 0); } + + Future updateTeamIds(String tournamentId, List teamIds) async { + try { + await _tournamentCollection + .doc(tournamentId) + .update({FireStoreConst.teamIds: teamIds}); + } catch (error, stack) { + throw AppError.fromError(error, stack); + } + } } diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 930736ff..6cd05cf1 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -215,6 +215,10 @@ "tournament_detail_overview_key_stats_title": "Key Stats", "tournament_detail_overview_featured_matches_title": "Featured Matches", + "tournament_detail_teams_empty_title": "Create teams!", + "tournament_detail_teams_empty_description": "You don't have a teams yet. Create two to get started.", + "tournament_detail_teams_add_btn": "Add teams", + "tournament_detail_key_stat_most_runs_title": "Most Runs", "tournament_detail_key_stat_most_wickets_title": "Most Wickets", "tournament_detail_key_stat_most_fours_title": "Most Fours", diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart index b1ee7eff..60391b50 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart @@ -32,18 +32,15 @@ class _TournamentDetailOverviewTabState extends ConsumerState { @override Widget build(BuildContext context) { - return Container( - color: context.colorScheme.containerLow, - child: ListView( - padding: context.mediaQueryPadding.copyWith(top: 0) + - EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 40), - children: [ - _featuredMatchesView(context, widget.tournament.matches), - _keyStatsView(context, widget.tournament.keyStats), - _teamsSquadsView(context, widget.tournament.teams), - _infoView(context, widget.tournament), - ], - ), + return ListView( + padding: context.mediaQueryPadding.copyWith(top: 0) + + EdgeInsets.symmetric(horizontal: 16).copyWith(bottom: 40), + children: [ + _featuredMatchesView(context, widget.tournament.matches), + _keyStatsView(context, widget.tournament.keyStats), + _teamsSquadsView(context, widget.tournament.teams), + _infoView(context, widget.tournament), + ], ); } @@ -258,7 +255,7 @@ class _TournamentDetailOverviewTabState children: [ _header( context, - showViewAll: teams.length >= 3, + showViewAll: teams.length > 3, title: context.l10n.tournament_detail_overview_teams_squads_title, onViewAll: () {}, ), diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart new file mode 100644 index 00000000..95155622 --- /dev/null +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart @@ -0,0 +1,112 @@ +import 'package:data/api/team/team_model.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:khelo/domain/extensions/context_extensions.dart'; +import 'package:khelo/domain/extensions/string_extensions.dart'; +import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart'; +import 'package:style/animations/on_tap_scale.dart'; +import 'package:style/button/bottom_sticky_overlay.dart'; +import 'package:style/button/primary_button.dart'; +import 'package:style/extensions/context_extensions.dart'; +import 'package:style/text/app_text_style.dart'; + +import '../../../../../components/empty_screen.dart'; +import '../../../../../components/image_avatar.dart'; +import '../../../../../gen/assets.gen.dart'; +import '../../../../app_route.dart'; + +class TournamentDetailTeamsTab extends ConsumerWidget { + final List teams; + final TournamentDetailStateViewNotifier notifier; + + const TournamentDetailTeamsTab({ + super.key, + required this.teams, + required this.notifier, + }); + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (teams.isEmpty) { + return Stack( + children: [ + EmptyScreen( + title: context.l10n.tournament_detail_teams_empty_title, + description: context.l10n.tournament_detail_teams_empty_description, + isShowButton: false, + ), + _stickyButton(context), + ], + ); + } + + return ListView.separated( + itemCount: teams.length, + padding: context.mediaQueryPadding.copyWith(top: 0) + + EdgeInsets.all(16).copyWith(bottom: 24), + itemBuilder: (context, index) { + return _teamCellView(context, teams[index]); + }, + separatorBuilder: (context, index) => SizedBox(height: 16), + ); + } + + Widget _teamCellView(BuildContext context, TeamModel team) { + return OnTapScale( + onTap: () => AppRoute.teamDetail(teamId: team.id).push(context), + child: Material( + type: MaterialType.transparency, + child: ListTile( + tileColor: context.colorScheme.surface, + shape: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + leading: ImageAvatar( + initial: team.name_initial ?? team.name.initials(limit: 1), + imageUrl: team.profile_img_url, + size: 40, + ), + title: Text( + team.name, + style: AppTextStyle.subtitle2 + .copyWith(color: context.colorScheme.textPrimary), + ), + subtitle: team.players.isNotEmpty + ? Padding( + padding: const EdgeInsets.only(top: 4), + child: Text( + '${team.players.length} ${context.l10n.add_team_players_text}', + style: AppTextStyle.body2 + .copyWith(color: context.colorScheme.textDisabled)), + ) + : null, + trailing: SvgPicture.asset( + Assets.images.icArrowForward, + colorFilter: ColorFilter.mode( + context.colorScheme.textDisabled, + BlendMode.srcATop, + ), + ), + ), + ), + ); + } + + Widget _stickyButton(BuildContext context) { + return BottomStickyOverlay( + child: PrimaryButton( + context.l10n.tournament_detail_teams_add_btn, + onPressed: () async { + final selectedTeams = + await AppRoute.teamSelection(selectedTeams: teams) + .push>(context); + if (context.mounted && selectedTeams != null) { + notifier.addTeams(selectedTeams); + } + }, + ), + ); + } +} diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index a9616334..c6deea65 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -9,6 +9,7 @@ import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/formatter/date_formatter.dart'; import 'package:khelo/ui/flow/tournament/components/sliver_header_delegate.dart'; import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_overview_tab.dart'; +import 'package:khelo/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart'; import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart'; import 'package:style/button/more_option_button.dart'; import 'package:style/button/tab_button.dart'; @@ -18,6 +19,7 @@ import 'package:style/text/app_text_style.dart'; import '../../../../components/app_page.dart'; import '../../../../components/error_screen.dart'; +import '../../../../components/error_snackbar.dart'; import '../../../../domain/extensions/widget_extension.dart'; import '../../../../gen/assets.gen.dart'; @@ -53,9 +55,22 @@ class _TournamentDetailScreenState runPostFrame(() => notifier.setData(widget.tournamentId)); } + void _observeActionError(BuildContext context) { + ref.listen( + tournamentDetailStateProvider.select((value) => value.actionError), + (previous, next) { + if (next != null) { + showErrorSnackBar(context: context, error: next); + } + }); + } + @override Widget build(BuildContext context) { + _observeActionError(context); + final state = ref.watch(tournamentDetailStateProvider); + return AppPage( body: Builder(builder: (context) { return _body(context, state); @@ -108,15 +123,22 @@ class _TournamentDetailScreenState } Widget _content(BuildContext context, TournamentDetailState state) { - return PageView( - controller: _controller, - physics: const NeverScrollableScrollPhysics(), - onPageChanged: notifier.onTabChange, - children: [ - TournamentDetailOverviewTab( - tournament: state.tournament!, - ), - ], + return Container( + color: context.colorScheme.containerLow, + child: PageView( + controller: _controller, + physics: const NeverScrollableScrollPhysics(), + onPageChanged: notifier.onTabChange, + children: [ + TournamentDetailOverviewTab( + tournament: state.tournament!, + ), + TournamentDetailTeamsTab( + teams: state.tournament?.teams ?? [], + notifier: notifier, + ), + ], + ), ); } diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart index e26eedb9..6110ccaa 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -1,5 +1,6 @@ import 'dart:async'; +import 'package:data/api/team/team_model.dart'; import 'package:data/api/tournament/tournament_model.dart'; import 'package:data/service/tournament/tournament_service.dart'; import 'package:flutter/material.dart'; @@ -52,6 +53,18 @@ class TournamentDetailStateViewNotifier } } + void addTeams(List teams) async { + if (state.tournament == null) return; + try { + final teamIds = teams.map((e) => e.id).toList(); + await _tournamentService.updateTeamIds(state.tournament!.id, teamIds); + } catch (e) { + state = state.copyWith(actionError: e); + debugPrint( + "TournamentDetailStateViewNotifier: error while adding teams -> $e"); + } + } + @override void dispose() { _tournamentSubscription?.cancel(); @@ -66,5 +79,6 @@ class TournamentDetailState with _$TournamentDetailState { @Default(false) bool loading, @Default(0) int selectedTab, Object? error, + Object? actionError, }) = _TournamentDetailState; } diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart index cfeb2014..3293ff9d 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.freezed.dart @@ -20,6 +20,7 @@ mixin _$TournamentDetailState { bool get loading => throw _privateConstructorUsedError; int get selectedTab => throw _privateConstructorUsedError; Object? get error => throw _privateConstructorUsedError; + Object? get actionError => throw _privateConstructorUsedError; /// Create a copy of TournamentDetailState /// with the given fields replaced by the non-null parameter values. @@ -38,7 +39,8 @@ abstract class $TournamentDetailStateCopyWith<$Res> { {TournamentModel? tournament, bool loading, int selectedTab, - Object? error}); + Object? error, + Object? actionError}); $TournamentModelCopyWith<$Res>? get tournament; } @@ -63,6 +65,7 @@ class _$TournamentDetailStateCopyWithImpl<$Res, Object? loading = null, Object? selectedTab = null, Object? error = freezed, + Object? actionError = freezed, }) { return _then(_value.copyWith( tournament: freezed == tournament @@ -78,6 +81,7 @@ class _$TournamentDetailStateCopyWithImpl<$Res, : selectedTab // ignore: cast_nullable_to_non_nullable as int, error: freezed == error ? _value.error : error, + actionError: freezed == actionError ? _value.actionError : actionError, ) as $Val); } @@ -109,7 +113,8 @@ abstract class _$$TournamentDetailStateImplCopyWith<$Res> {TournamentModel? tournament, bool loading, int selectedTab, - Object? error}); + Object? error, + Object? actionError}); @override $TournamentModelCopyWith<$Res>? get tournament; @@ -133,6 +138,7 @@ class __$$TournamentDetailStateImplCopyWithImpl<$Res> Object? loading = null, Object? selectedTab = null, Object? error = freezed, + Object? actionError = freezed, }) { return _then(_$TournamentDetailStateImpl( tournament: freezed == tournament @@ -148,6 +154,7 @@ class __$$TournamentDetailStateImplCopyWithImpl<$Res> : selectedTab // ignore: cast_nullable_to_non_nullable as int, error: freezed == error ? _value.error : error, + actionError: freezed == actionError ? _value.actionError : actionError, )); } } @@ -159,7 +166,8 @@ class _$TournamentDetailStateImpl implements _TournamentDetailState { {this.tournament = null, this.loading = false, this.selectedTab = 0, - this.error}); + this.error, + this.actionError}); @override @JsonKey() @@ -172,10 +180,12 @@ class _$TournamentDetailStateImpl implements _TournamentDetailState { final int selectedTab; @override final Object? error; + @override + final Object? actionError; @override String toString() { - return 'TournamentDetailState(tournament: $tournament, loading: $loading, selectedTab: $selectedTab, error: $error)'; + return 'TournamentDetailState(tournament: $tournament, loading: $loading, selectedTab: $selectedTab, error: $error, actionError: $actionError)'; } @override @@ -188,12 +198,19 @@ class _$TournamentDetailStateImpl implements _TournamentDetailState { (identical(other.loading, loading) || other.loading == loading) && (identical(other.selectedTab, selectedTab) || other.selectedTab == selectedTab) && - const DeepCollectionEquality().equals(other.error, error)); + const DeepCollectionEquality().equals(other.error, error) && + const DeepCollectionEquality() + .equals(other.actionError, actionError)); } @override - int get hashCode => Object.hash(runtimeType, tournament, loading, selectedTab, - const DeepCollectionEquality().hash(error)); + int get hashCode => Object.hash( + runtimeType, + tournament, + loading, + selectedTab, + const DeepCollectionEquality().hash(error), + const DeepCollectionEquality().hash(actionError)); /// Create a copy of TournamentDetailState /// with the given fields replaced by the non-null parameter values. @@ -210,7 +227,8 @@ abstract class _TournamentDetailState implements TournamentDetailState { {final TournamentModel? tournament, final bool loading, final int selectedTab, - final Object? error}) = _$TournamentDetailStateImpl; + final Object? error, + final Object? actionError}) = _$TournamentDetailStateImpl; @override TournamentModel? get tournament; @@ -220,6 +238,8 @@ abstract class _TournamentDetailState implements TournamentDetailState { int get selectedTab; @override Object? get error; + @override + Object? get actionError; /// Create a copy of TournamentDetailState /// with the given fields replaced by the non-null parameter values. From f179a4d0923522ec673b3de15ec20e6caca59066 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 23 Oct 2024 15:57:51 +0530 Subject: [PATCH 32/33] Mr changes --- data/lib/service/match/match_service.dart | 4 +--- khelo/assets/locales/app_en.arb | 6 +++--- .../detail/tabs/tournament_detail_teams_tab.dart | 9 ++++----- .../flow/tournament/detail/tournament_detail_screen.dart | 3 +-- .../tournament/detail/tournament_detail_view_model.dart | 4 ++-- 5 files changed, 11 insertions(+), 15 deletions(-) diff --git a/data/lib/service/match/match_service.dart b/data/lib/service/match/match_service.dart index 57e9e1c4..4c715162 100644 --- a/data/lib/service/match/match_service.dart +++ b/data/lib/service/match/match_service.dart @@ -534,9 +534,7 @@ class MatchService { try { final teamIds = teamList.map((e) => e.team_id).toList(); - final playerIds = teamList - .expand((team) => team.squad.map((player) => player)) - .toList(); + final playerIds = teamList.expand((team) => team.squad).toList(); final teamsData = await Future.wait([ _teamService.getTeamsByIds(teamIds), diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 6cd05cf1..8ef60ada 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -215,9 +215,9 @@ "tournament_detail_overview_key_stats_title": "Key Stats", "tournament_detail_overview_featured_matches_title": "Featured Matches", - "tournament_detail_teams_empty_title": "Create teams!", - "tournament_detail_teams_empty_description": "You don't have a teams yet. Create two to get started.", - "tournament_detail_teams_add_btn": "Add teams", + "tournament_detail_teams_empty_title": "Select Your Teams!", + "tournament_detail_teams_empty_description": "You haven't added any teams to this tournament yet. Please select or create teams to get started.", + "tournament_detail_teams_select_btn": "Select teams", "tournament_detail_key_stat_most_runs_title": "Most Runs", "tournament_detail_key_stat_most_wickets_title": "Most Wickets", diff --git a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart index 95155622..89f79d38 100644 --- a/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart +++ b/khelo/lib/ui/flow/tournament/detail/tabs/tournament_detail_teams_tab.dart @@ -4,7 +4,6 @@ import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_svg/svg.dart'; import 'package:khelo/domain/extensions/context_extensions.dart'; import 'package:khelo/domain/extensions/string_extensions.dart'; -import 'package:khelo/ui/flow/tournament/detail/tournament_detail_view_model.dart'; import 'package:style/animations/on_tap_scale.dart'; import 'package:style/button/bottom_sticky_overlay.dart'; import 'package:style/button/primary_button.dart'; @@ -18,12 +17,12 @@ import '../../../../app_route.dart'; class TournamentDetailTeamsTab extends ConsumerWidget { final List teams; - final TournamentDetailStateViewNotifier notifier; + final Function(List) onSelected; const TournamentDetailTeamsTab({ super.key, required this.teams, - required this.notifier, + required this.onSelected, }); @override @@ -97,13 +96,13 @@ class TournamentDetailTeamsTab extends ConsumerWidget { Widget _stickyButton(BuildContext context) { return BottomStickyOverlay( child: PrimaryButton( - context.l10n.tournament_detail_teams_add_btn, + context.l10n.tournament_detail_teams_select_btn, onPressed: () async { final selectedTeams = await AppRoute.teamSelection(selectedTeams: teams) .push>(context); if (context.mounted && selectedTeams != null) { - notifier.addTeams(selectedTeams); + onSelected.call(selectedTeams); } }, ), diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart index c6deea65..ea8718e6 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_screen.dart @@ -127,7 +127,6 @@ class _TournamentDetailScreenState color: context.colorScheme.containerLow, child: PageView( controller: _controller, - physics: const NeverScrollableScrollPhysics(), onPageChanged: notifier.onTabChange, children: [ TournamentDetailOverviewTab( @@ -135,7 +134,7 @@ class _TournamentDetailScreenState ), TournamentDetailTeamsTab( teams: state.tournament?.teams ?? [], - notifier: notifier, + onSelected: notifier.onTeamsSelected, ), ], ), diff --git a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart index 6110ccaa..072af1e1 100644 --- a/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart +++ b/khelo/lib/ui/flow/tournament/detail/tournament_detail_view_model.dart @@ -53,7 +53,7 @@ class TournamentDetailStateViewNotifier } } - void addTeams(List teams) async { + void onTeamsSelected(List teams) async { if (state.tournament == null) return; try { final teamIds = teams.map((e) => e.id).toList(); @@ -61,7 +61,7 @@ class TournamentDetailStateViewNotifier } catch (e) { state = state.copyWith(actionError: e); debugPrint( - "TournamentDetailStateViewNotifier: error while adding teams -> $e"); + "TournamentDetailStateViewNotifier: error while selecting teams -> $e"); } } From b1d3a2f8f0a4423c567a0abe17c11e31d2c0f7f7 Mon Sep 17 00:00:00 2001 From: Mayank Variya Date: Wed, 23 Oct 2024 16:16:43 +0530 Subject: [PATCH 33/33] Minor changes --- khelo/assets/locales/app_en.arb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/khelo/assets/locales/app_en.arb b/khelo/assets/locales/app_en.arb index 8ef60ada..b00cdd41 100644 --- a/khelo/assets/locales/app_en.arb +++ b/khelo/assets/locales/app_en.arb @@ -217,7 +217,7 @@ "tournament_detail_teams_empty_title": "Select Your Teams!", "tournament_detail_teams_empty_description": "You haven't added any teams to this tournament yet. Please select or create teams to get started.", - "tournament_detail_teams_select_btn": "Select teams", + "tournament_detail_teams_select_btn": "Add teams", "tournament_detail_key_stat_most_runs_title": "Most Runs", "tournament_detail_key_stat_most_wickets_title": "Most Wickets",