Game Prize and Logout

This commit is contained in:
efrilm 2025-09-18 10:39:54 +07:00
parent 006486bc2a
commit 3c596461c6
31 changed files with 4189 additions and 677 deletions

View File

@ -0,0 +1,37 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/auth/auth.dart';
part 'logout_form_event.dart';
part 'logout_form_state.dart';
part 'logout_form_bloc.freezed.dart';
@injectable
class LogoutFormBloc extends Bloc<LogoutFormEvent, LogoutFormState> {
final IAuthRepository _repository;
LogoutFormBloc(this._repository) : super(LogoutFormState.initial()) {
on<LogoutFormEvent>(_onLogoutFormEvent);
}
Future<void> _onLogoutFormEvent(
LogoutFormEvent event,
Emitter<LogoutFormState> emit,
) {
return event.map(
submitted: (e) async {
emit(state.copyWith(isSubmitting: true, failureOrAuthOption: none()));
final failureOrAuth = await _repository.logout();
emit(
state.copyWith(
isSubmitting: false,
failureOrAuthOption: optionOf(failureOrAuth),
),
);
},
);
}
}

View File

@ -0,0 +1,335 @@
// 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 'logout_form_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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 _$LogoutFormEvent {
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Submitted value) submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Submitted value)? submitted,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LogoutFormEventCopyWith<$Res> {
factory $LogoutFormEventCopyWith(
LogoutFormEvent value,
$Res Function(LogoutFormEvent) then,
) = _$LogoutFormEventCopyWithImpl<$Res, LogoutFormEvent>;
}
/// @nodoc
class _$LogoutFormEventCopyWithImpl<$Res, $Val extends LogoutFormEvent>
implements $LogoutFormEventCopyWith<$Res> {
_$LogoutFormEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LogoutFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
abstract class _$$SubmittedImplCopyWith<$Res> {
factory _$$SubmittedImplCopyWith(
_$SubmittedImpl value,
$Res Function(_$SubmittedImpl) then,
) = __$$SubmittedImplCopyWithImpl<$Res>;
}
/// @nodoc
class __$$SubmittedImplCopyWithImpl<$Res>
extends _$LogoutFormEventCopyWithImpl<$Res, _$SubmittedImpl>
implements _$$SubmittedImplCopyWith<$Res> {
__$$SubmittedImplCopyWithImpl(
_$SubmittedImpl _value,
$Res Function(_$SubmittedImpl) _then,
) : super(_value, _then);
/// Create a copy of LogoutFormEvent
/// with the given fields replaced by the non-null parameter values.
}
/// @nodoc
class _$SubmittedImpl implements _Submitted {
const _$SubmittedImpl();
@override
String toString() {
return 'LogoutFormEvent.submitted()';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType && other is _$SubmittedImpl);
}
@override
int get hashCode => runtimeType.hashCode;
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function() submitted,
}) {
return submitted();
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function()? submitted,
}) {
return submitted?.call();
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function()? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted();
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Submitted value) submitted,
}) {
return submitted(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Submitted value)? submitted,
}) {
return submitted?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Submitted value)? submitted,
required TResult orElse(),
}) {
if (submitted != null) {
return submitted(this);
}
return orElse();
}
}
abstract class _Submitted implements LogoutFormEvent {
const factory _Submitted() = _$SubmittedImpl;
}
/// @nodoc
mixin _$LogoutFormState {
Option<Either<AuthFailure, Unit>> get failureOrAuthOption =>
throw _privateConstructorUsedError;
bool get isSubmitting => throw _privateConstructorUsedError;
/// Create a copy of LogoutFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$LogoutFormStateCopyWith<LogoutFormState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $LogoutFormStateCopyWith<$Res> {
factory $LogoutFormStateCopyWith(
LogoutFormState value,
$Res Function(LogoutFormState) then,
) = _$LogoutFormStateCopyWithImpl<$Res, LogoutFormState>;
@useResult
$Res call({
Option<Either<AuthFailure, Unit>> failureOrAuthOption,
bool isSubmitting,
});
}
/// @nodoc
class _$LogoutFormStateCopyWithImpl<$Res, $Val extends LogoutFormState>
implements $LogoutFormStateCopyWith<$Res> {
_$LogoutFormStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of LogoutFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? failureOrAuthOption = null, Object? isSubmitting = null}) {
return _then(
_value.copyWith(
failureOrAuthOption: null == failureOrAuthOption
? _value.failureOrAuthOption
: failureOrAuthOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, Unit>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$LogoutFormStateImplCopyWith<$Res>
implements $LogoutFormStateCopyWith<$Res> {
factory _$$LogoutFormStateImplCopyWith(
_$LogoutFormStateImpl value,
$Res Function(_$LogoutFormStateImpl) then,
) = __$$LogoutFormStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
Option<Either<AuthFailure, Unit>> failureOrAuthOption,
bool isSubmitting,
});
}
/// @nodoc
class __$$LogoutFormStateImplCopyWithImpl<$Res>
extends _$LogoutFormStateCopyWithImpl<$Res, _$LogoutFormStateImpl>
implements _$$LogoutFormStateImplCopyWith<$Res> {
__$$LogoutFormStateImplCopyWithImpl(
_$LogoutFormStateImpl _value,
$Res Function(_$LogoutFormStateImpl) _then,
) : super(_value, _then);
/// Create a copy of LogoutFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? failureOrAuthOption = null, Object? isSubmitting = null}) {
return _then(
_$LogoutFormStateImpl(
failureOrAuthOption: null == failureOrAuthOption
? _value.failureOrAuthOption
: failureOrAuthOption // ignore: cast_nullable_to_non_nullable
as Option<Either<AuthFailure, Unit>>,
isSubmitting: null == isSubmitting
? _value.isSubmitting
: isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$LogoutFormStateImpl implements _LogoutFormState {
const _$LogoutFormStateImpl({
required this.failureOrAuthOption,
this.isSubmitting = false,
});
@override
final Option<Either<AuthFailure, Unit>> failureOrAuthOption;
@override
@JsonKey()
final bool isSubmitting;
@override
String toString() {
return 'LogoutFormState(failureOrAuthOption: $failureOrAuthOption, isSubmitting: $isSubmitting)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$LogoutFormStateImpl &&
(identical(other.failureOrAuthOption, failureOrAuthOption) ||
other.failureOrAuthOption == failureOrAuthOption) &&
(identical(other.isSubmitting, isSubmitting) ||
other.isSubmitting == isSubmitting));
}
@override
int get hashCode =>
Object.hash(runtimeType, failureOrAuthOption, isSubmitting);
/// Create a copy of LogoutFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$LogoutFormStateImplCopyWith<_$LogoutFormStateImpl> get copyWith =>
__$$LogoutFormStateImplCopyWithImpl<_$LogoutFormStateImpl>(
this,
_$identity,
);
}
abstract class _LogoutFormState implements LogoutFormState {
const factory _LogoutFormState({
required final Option<Either<AuthFailure, Unit>> failureOrAuthOption,
final bool isSubmitting,
}) = _$LogoutFormStateImpl;
@override
Option<Either<AuthFailure, Unit>> get failureOrAuthOption;
@override
bool get isSubmitting;
/// Create a copy of LogoutFormState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$LogoutFormStateImplCopyWith<_$LogoutFormStateImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,6 @@
part of 'logout_form_bloc.dart';
@freezed
class LogoutFormEvent with _$LogoutFormEvent {
const factory LogoutFormEvent.submitted() = _Submitted;
}

View File

@ -0,0 +1,12 @@
part of 'logout_form_bloc.dart';
@freezed
class LogoutFormState with _$LogoutFormState {
const factory LogoutFormState({
required Option<Either<AuthFailure, Unit>> failureOrAuthOption,
@Default(false) bool isSubmitting,
}) = _LogoutFormState;
factory LogoutFormState.initial() =>
LogoutFormState(failureOrAuthOption: none(), isSubmitting: false);
}

View File

@ -0,0 +1,40 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/game/game.dart';
part 'game_prize_loader_event.dart';
part 'game_prize_loader_state.dart';
part 'game_prize_loader_bloc.freezed.dart';
@injectable
class GamePrizeLoaderBloc
extends Bloc<GamePrizeLoaderEvent, GamePrizeLoaderState> {
final IGameRepository _repository;
GamePrizeLoaderBloc(this._repository)
: super(GamePrizeLoaderState.initial()) {
on<GamePrizeLoaderEvent>(_onGamePrizeLoaderEvent);
}
Future<void> _onGamePrizeLoaderEvent(
GamePrizeLoaderEvent event,
Emitter<GamePrizeLoaderState> emit,
) {
return event.map(
fetched: (e) async {
emit(state.copyWith(isFetching: true, failureOptionGamePrize: none()));
final result = await _repository.gamePrizeByGameId(id: e.id);
var data = result.fold(
(f) => state.copyWith(failureOptionGamePrize: optionOf(f)),
(gamePrize) => state.copyWith(gamePrize: gamePrize),
);
emit(data.copyWith(isFetching: false));
},
);
}
}

View File

@ -0,0 +1,442 @@
// 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 'game_prize_loader_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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 _$GamePrizeLoaderEvent {
String get id => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) => throw _privateConstructorUsedError;
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) => throw _privateConstructorUsedError;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GamePrizeLoaderEventCopyWith<GamePrizeLoaderEvent> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GamePrizeLoaderEventCopyWith<$Res> {
factory $GamePrizeLoaderEventCopyWith(
GamePrizeLoaderEvent value,
$Res Function(GamePrizeLoaderEvent) then,
) = _$GamePrizeLoaderEventCopyWithImpl<$Res, GamePrizeLoaderEvent>;
@useResult
$Res call({String id});
}
/// @nodoc
class _$GamePrizeLoaderEventCopyWithImpl<
$Res,
$Val extends GamePrizeLoaderEvent
>
implements $GamePrizeLoaderEventCopyWith<$Res> {
_$GamePrizeLoaderEventCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? id = null}) {
return _then(
_value.copyWith(
id: null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$FetchedImplCopyWith<$Res>
implements $GamePrizeLoaderEventCopyWith<$Res> {
factory _$$FetchedImplCopyWith(
_$FetchedImpl value,
$Res Function(_$FetchedImpl) then,
) = __$$FetchedImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({String id});
}
/// @nodoc
class __$$FetchedImplCopyWithImpl<$Res>
extends _$GamePrizeLoaderEventCopyWithImpl<$Res, _$FetchedImpl>
implements _$$FetchedImplCopyWith<$Res> {
__$$FetchedImplCopyWithImpl(
_$FetchedImpl _value,
$Res Function(_$FetchedImpl) _then,
) : super(_value, _then);
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({Object? id = null}) {
return _then(
_$FetchedImpl(
null == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String,
),
);
}
}
/// @nodoc
class _$FetchedImpl implements _Fetched {
const _$FetchedImpl(this.id);
@override
final String id;
@override
String toString() {
return 'GamePrizeLoaderEvent.fetched(id: $id)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$FetchedImpl &&
(identical(other.id, id) || other.id == id));
}
@override
int get hashCode => Object.hash(runtimeType, id);
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
__$$FetchedImplCopyWithImpl<_$FetchedImpl>(this, _$identity);
@override
@optionalTypeArgs
TResult when<TResult extends Object?>({
required TResult Function(String id) fetched,
}) {
return fetched(id);
}
@override
@optionalTypeArgs
TResult? whenOrNull<TResult extends Object?>({
TResult? Function(String id)? fetched,
}) {
return fetched?.call(id);
}
@override
@optionalTypeArgs
TResult maybeWhen<TResult extends Object?>({
TResult Function(String id)? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched(id);
}
return orElse();
}
@override
@optionalTypeArgs
TResult map<TResult extends Object?>({
required TResult Function(_Fetched value) fetched,
}) {
return fetched(this);
}
@override
@optionalTypeArgs
TResult? mapOrNull<TResult extends Object?>({
TResult? Function(_Fetched value)? fetched,
}) {
return fetched?.call(this);
}
@override
@optionalTypeArgs
TResult maybeMap<TResult extends Object?>({
TResult Function(_Fetched value)? fetched,
required TResult orElse(),
}) {
if (fetched != null) {
return fetched(this);
}
return orElse();
}
}
abstract class _Fetched implements GamePrizeLoaderEvent {
const factory _Fetched(final String id) = _$FetchedImpl;
@override
String get id;
/// Create a copy of GamePrizeLoaderEvent
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$FetchedImplCopyWith<_$FetchedImpl> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
mixin _$GamePrizeLoaderState {
List<GamePrize> get gamePrize => throw _privateConstructorUsedError;
Option<GameFailure> get failureOptionGamePrize =>
throw _privateConstructorUsedError;
bool get isFetching => throw _privateConstructorUsedError;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GamePrizeLoaderStateCopyWith<GamePrizeLoaderState> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GamePrizeLoaderStateCopyWith<$Res> {
factory $GamePrizeLoaderStateCopyWith(
GamePrizeLoaderState value,
$Res Function(GamePrizeLoaderState) then,
) = _$GamePrizeLoaderStateCopyWithImpl<$Res, GamePrizeLoaderState>;
@useResult
$Res call({
List<GamePrize> gamePrize,
Option<GameFailure> failureOptionGamePrize,
bool isFetching,
});
}
/// @nodoc
class _$GamePrizeLoaderStateCopyWithImpl<
$Res,
$Val extends GamePrizeLoaderState
>
implements $GamePrizeLoaderStateCopyWith<$Res> {
_$GamePrizeLoaderStateCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? gamePrize = null,
Object? failureOptionGamePrize = null,
Object? isFetching = null,
}) {
return _then(
_value.copyWith(
gamePrize: null == gamePrize
? _value.gamePrize
: gamePrize // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
failureOptionGamePrize: null == failureOptionGamePrize
? _value.failureOptionGamePrize
: failureOptionGamePrize // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$GamePrizeLoaderStateImplCopyWith<$Res>
implements $GamePrizeLoaderStateCopyWith<$Res> {
factory _$$GamePrizeLoaderStateImplCopyWith(
_$GamePrizeLoaderStateImpl value,
$Res Function(_$GamePrizeLoaderStateImpl) then,
) = __$$GamePrizeLoaderStateImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
List<GamePrize> gamePrize,
Option<GameFailure> failureOptionGamePrize,
bool isFetching,
});
}
/// @nodoc
class __$$GamePrizeLoaderStateImplCopyWithImpl<$Res>
extends _$GamePrizeLoaderStateCopyWithImpl<$Res, _$GamePrizeLoaderStateImpl>
implements _$$GamePrizeLoaderStateImplCopyWith<$Res> {
__$$GamePrizeLoaderStateImplCopyWithImpl(
_$GamePrizeLoaderStateImpl _value,
$Res Function(_$GamePrizeLoaderStateImpl) _then,
) : super(_value, _then);
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? gamePrize = null,
Object? failureOptionGamePrize = null,
Object? isFetching = null,
}) {
return _then(
_$GamePrizeLoaderStateImpl(
gamePrize: null == gamePrize
? _value._gamePrize
: gamePrize // ignore: cast_nullable_to_non_nullable
as List<GamePrize>,
failureOptionGamePrize: null == failureOptionGamePrize
? _value.failureOptionGamePrize
: failureOptionGamePrize // ignore: cast_nullable_to_non_nullable
as Option<GameFailure>,
isFetching: null == isFetching
? _value.isFetching
: isFetching // ignore: cast_nullable_to_non_nullable
as bool,
),
);
}
}
/// @nodoc
class _$GamePrizeLoaderStateImpl implements _GamePrizeLoaderState {
const _$GamePrizeLoaderStateImpl({
required final List<GamePrize> gamePrize,
required this.failureOptionGamePrize,
this.isFetching = false,
}) : _gamePrize = gamePrize;
final List<GamePrize> _gamePrize;
@override
List<GamePrize> get gamePrize {
if (_gamePrize is EqualUnmodifiableListView) return _gamePrize;
// ignore: implicit_dynamic_type
return EqualUnmodifiableListView(_gamePrize);
}
@override
final Option<GameFailure> failureOptionGamePrize;
@override
@JsonKey()
final bool isFetching;
@override
String toString() {
return 'GamePrizeLoaderState(gamePrize: $gamePrize, failureOptionGamePrize: $failureOptionGamePrize, isFetching: $isFetching)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GamePrizeLoaderStateImpl &&
const DeepCollectionEquality().equals(
other._gamePrize,
_gamePrize,
) &&
(identical(other.failureOptionGamePrize, failureOptionGamePrize) ||
other.failureOptionGamePrize == failureOptionGamePrize) &&
(identical(other.isFetching, isFetching) ||
other.isFetching == isFetching));
}
@override
int get hashCode => Object.hash(
runtimeType,
const DeepCollectionEquality().hash(_gamePrize),
failureOptionGamePrize,
isFetching,
);
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GamePrizeLoaderStateImplCopyWith<_$GamePrizeLoaderStateImpl>
get copyWith =>
__$$GamePrizeLoaderStateImplCopyWithImpl<_$GamePrizeLoaderStateImpl>(
this,
_$identity,
);
}
abstract class _GamePrizeLoaderState implements GamePrizeLoaderState {
const factory _GamePrizeLoaderState({
required final List<GamePrize> gamePrize,
required final Option<GameFailure> failureOptionGamePrize,
final bool isFetching,
}) = _$GamePrizeLoaderStateImpl;
@override
List<GamePrize> get gamePrize;
@override
Option<GameFailure> get failureOptionGamePrize;
@override
bool get isFetching;
/// Create a copy of GamePrizeLoaderState
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GamePrizeLoaderStateImplCopyWith<_$GamePrizeLoaderStateImpl>
get copyWith => throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,6 @@
part of 'game_prize_loader_bloc.dart';
@freezed
class GamePrizeLoaderEvent with _$GamePrizeLoaderEvent {
const factory GamePrizeLoaderEvent.fetched(String id) = _Fetched;
}

View File

@ -0,0 +1,13 @@
part of 'game_prize_loader_bloc.dart';
@freezed
class GamePrizeLoaderState with _$GamePrizeLoaderState {
const factory GamePrizeLoaderState({
required List<GamePrize> gamePrize,
required Option<GameFailure> failureOptionGamePrize,
@Default(false) bool isFetching,
}) = _GamePrizeLoaderState;
factory GamePrizeLoaderState.initial() =>
GamePrizeLoaderState(gamePrize: [], failureOptionGamePrize: none());
}

View File

@ -1,15 +1,40 @@
import 'package:dio/dio.dart'; import 'dart:developer';
import 'package:auto_route/auto_route.dart';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../../presentation/router/app_router.gr.dart';
import '../../constant/local_storage_key.dart';
import '../errors/unauthorized_error.dart'; import '../errors/unauthorized_error.dart';
class UnauthorizedInterceptor extends Interceptor { class UnauthorizedInterceptor extends Interceptor {
static final GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
@override @override
void onError(DioException err, ErrorInterceptorHandler handler) { void onError(DioException err, ErrorInterceptorHandler handler) async {
if (err.response?.statusCode == 401 || if (err.response?.statusCode == 401 ||
err.response?.statusCode == 403 || err.response?.statusCode == 403 ||
err.response?.statusCode == 419) { err.response?.statusCode == 419) {
await _handleTokenExpired();
return super.onError(UnauthorizedError(err, null), handler); return super.onError(UnauthorizedError(err, null), handler);
} }
super.onError(err, handler); super.onError(err, handler);
} }
Future<void> _handleTokenExpired() async {
// Clear stored token
final prefs = await SharedPreferences.getInstance();
await prefs.remove(LocalStorageKey.token);
await prefs.remove(LocalStorageKey.user);
await prefs.clear(); // Optional: clear all user data
log('handleTokenExpired');
// Navigate to login page
final context = navigatorKey.currentContext;
if (context != null) {
// Option 1: Navigate and remove all previous routes
context.router.replaceAll([LoginRoute()]);
}
}
} }

View File

@ -1,4 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../injection.dart';
import '../constant/local_storage_key.dart';
void dismissKeyboard(BuildContext context) { void dismissKeyboard(BuildContext context) {
final currentFocus = FocusScope.of(context); final currentFocus = FocusScope.of(context);
@ -13,3 +17,10 @@ String getNormalizePhone(String phoneNumber) {
: phoneNumber; : phoneNumber;
return '62$normalizedPhone'; return '62$normalizedPhone';
} }
Map<String, dynamic> getAuthorizationHeader() {
return {
'Authorization':
'Bearer ${getIt<SharedPreferences>().getString(LocalStorageKey.token)}',
};
}

View File

@ -1,19 +1,22 @@
import 'dart:math' as math; import 'dart:math' as math;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../presentation/pages/mini_games/ferris_wheel/data/model.dart'; import '../../domain/game/game.dart';
import '../theme/theme.dart'; import '../theme/theme.dart';
class WheelPainter extends CustomPainter { class WheelPainter extends CustomPainter {
final List<WheelSection> sections; final List<GamePrize> gamePrizes;
final Color Function(GamePrize prize, int index) getPrizeColor;
WheelPainter({required this.sections}); WheelPainter({required this.gamePrizes, required this.getPrizeColor});
@override @override
void paint(Canvas canvas, Size size) { void paint(Canvas canvas, Size size) {
if (gamePrizes.isEmpty) return;
final center = Offset(size.width / 2, size.height / 2); final center = Offset(size.width / 2, size.height / 2);
final radius = size.width / 2; final radius = size.width / 2;
final sectionAngle = 2 * math.pi / sections.length; final sectionAngle = 2 * math.pi / gamePrizes.length;
// Draw outer white border // Draw outer white border
final outerBorderPaint = Paint() final outerBorderPaint = Paint()
@ -34,12 +37,13 @@ class WheelPainter extends CustomPainter {
canvas.drawCircle(center, radius - 20, innerWhitePaint); canvas.drawCircle(center, radius - 20, innerWhitePaint);
// Draw sections // Draw sections
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < gamePrizes.length; i++) {
final prize = gamePrizes[i];
final startAngle = i * sectionAngle - math.pi / 2; final startAngle = i * sectionAngle - math.pi / 2;
// Section background // Section background
final sectionPaint = Paint() final sectionPaint = Paint()
..color = sections[i].color ..color = getPrizeColor(prize, i)
..style = PaintingStyle.fill; ..style = PaintingStyle.fill;
canvas.drawArc( canvas.drawArc(
@ -67,15 +71,16 @@ class WheelPainter extends CustomPainter {
canvas.save(); canvas.save();
canvas.translate(iconPosition.dx, iconPosition.dy); canvas.translate(iconPosition.dx, iconPosition.dy);
// Draw icon using TextPainter to simulate Icon widget // Get icon from metadata or use default
final iconText = _getIconText(sections[i].icon); final iconData = _getIconFromMetadata(prize.metadata);
final iconText = _getIconText(iconData);
final iconTextPainter = TextPainter( final iconTextPainter = TextPainter(
text: TextSpan( text: TextSpan(
text: iconText, text: iconText,
style: TextStyle( style: TextStyle(
fontFamily: 'MaterialIcons', fontFamily: 'MaterialIcons',
fontSize: 20, fontSize: 20,
color: sections[i].color, color: getPrizeColor(prize, i),
fontWeight: FontWeight.normal, fontWeight: FontWeight.normal,
), ),
), ),
@ -103,7 +108,7 @@ class WheelPainter extends CustomPainter {
final textPainter = TextPainter( final textPainter = TextPainter(
text: TextSpan( text: TextSpan(
text: sections[i].prize, text: prize.name,
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: Colors.white,
fontSize: 10, fontSize: 10,
@ -119,6 +124,42 @@ class WheelPainter extends CustomPainter {
); );
canvas.restore(); canvas.restore();
// Draw stock indicator if stock is low
if (prize.stock <= 5 && prize.stock > 0) {
final stockAngle = startAngle + sectionAngle / 2;
final stockPosition = Offset(
center.dx + (radius - 35) * math.cos(stockAngle),
center.dy + (radius - 35) * math.sin(stockAngle),
);
final stockPaint = Paint()
..color = AppColor.error
..style = PaintingStyle.fill;
canvas.drawCircle(stockPosition, 8, stockPaint);
canvas.save();
canvas.translate(stockPosition.dx, stockPosition.dy);
final stockTextPainter = TextPainter(
text: TextSpan(
text: '!',
style: const TextStyle(
color: Colors.white,
fontSize: 10,
fontWeight: FontWeight.bold,
),
),
textDirection: TextDirection.ltr,
);
stockTextPainter.layout();
stockTextPainter.paint(
canvas,
Offset(-stockTextPainter.width / 2, -stockTextPainter.height / 2),
);
canvas.restore();
}
} }
// Draw white dots around the outer edge // Draw white dots around the outer edge
@ -141,7 +182,7 @@ class WheelPainter extends CustomPainter {
..style = PaintingStyle.stroke ..style = PaintingStyle.stroke
..strokeWidth = 2; ..strokeWidth = 2;
for (int i = 0; i < sections.length; i++) { for (int i = 0; i < gamePrizes.length; i++) {
final angle = i * sectionAngle - math.pi / 2; final angle = i * sectionAngle - math.pi / 2;
final lineStart = Offset( final lineStart = Offset(
center.dx + (radius - 130) * math.cos(angle), center.dx + (radius - 130) * math.cos(angle),
@ -155,6 +196,34 @@ class WheelPainter extends CustomPainter {
} }
} }
IconData _getIconFromMetadata(Map<String, dynamic> metadata) {
final iconName = metadata['icon'] as String?;
switch (iconName?.toLowerCase()) {
case 'card_giftcard':
return Icons.card_giftcard;
case 'monetization_on':
return Icons.monetization_on;
case 'redeem':
return Icons.redeem;
case 'attach_money':
return Icons.attach_money;
case 'account_balance_wallet':
return Icons.account_balance_wallet;
case 'diamond':
return Icons.diamond;
case 'star':
return Icons.star;
case 'emoji_events':
return Icons.emoji_events;
case 'refresh':
return Icons.refresh;
case 'visibility':
return Icons.visibility;
default:
return Icons.card_giftcard; // Default icon
}
}
String _getIconText(IconData icon) { String _getIconText(IconData icon) {
// Convert IconData to Unicode string for drawing // Convert IconData to Unicode string for drawing
switch (icon.codePoint) { switch (icon.codePoint) {
@ -174,11 +243,17 @@ class WheelPainter extends CustomPainter {
return String.fromCharCode(0xe8a1); return String.fromCharCode(0xe8a1);
case 0xe57d: // Icons.monetization_on case 0xe57d: // Icons.monetization_on
return String.fromCharCode(0xe57d); return String.fromCharCode(0xe57d);
case 0xe5ca: // Icons.diamond
return String.fromCharCode(0xe5ca);
case 0xe838: // Icons.star
return String.fromCharCode(0xe838);
case 0xe2a3: // Icons.emoji_events
return String.fromCharCode(0xe2a3);
default: default:
return String.fromCharCode(0xe87c); // Default star icon return String.fromCharCode(0xe151); // Default card_giftcard icon
} }
} }
@override @override
bool shouldRepaint(covariant CustomPainter oldDelegate) => false; bool shouldRepaint(covariant CustomPainter oldDelegate) => true;
} }

View File

@ -5,4 +5,7 @@ class ApiPath {
static String setPassword = '/api/v1/customer-auth/register/set-password'; static String setPassword = '/api/v1/customer-auth/register/set-password';
static String login = '/api/v1/customer-auth/login'; static String login = '/api/v1/customer-auth/login';
static String resend = '/api/v1/customer-auth/resend-otp'; static String resend = '/api/v1/customer-auth/resend-otp';
// Marketing
static String gamePrizeByGameId = '/api/v1/marketing/game-prizes/game';
} }

View File

@ -0,0 +1,24 @@
part of '../game.dart';
@freezed
class Game with _$Game {
const factory Game({
required String id,
required String name,
required String type,
required bool isActive,
required Map<String, dynamic> metadata,
required String createdAt,
required String updatedAt,
}) = _Game;
factory Game.empty() => const Game(
id: '',
name: '',
type: '',
isActive: false,
metadata: {},
createdAt: '',
updatedAt: '',
);
}

View File

@ -0,0 +1,32 @@
part of '../game.dart';
@freezed
class GamePrize with _$GamePrize {
const factory GamePrize({
required String id,
required String gameId,
required String name,
required int weight,
required int stock,
required int maxStock,
required int threshold,
required Map<String, dynamic> metadata,
required Game game,
required String createdAt,
required String updatedAt,
}) = _GamePrize;
factory GamePrize.empty() => GamePrize(
id: '',
gameId: '',
name: '',
weight: 0,
stock: 0,
maxStock: 0,
threshold: 0,
metadata: const {},
game: Game.empty(),
createdAt: '',
updatedAt: '',
);
}

View File

@ -0,0 +1,9 @@
part of '../game.dart';
@freezed
sealed class GameFailure with _$GameFailure {
const factory GameFailure.serverError(ApiFailure failure) = _ServerError;
const factory GameFailure.unexpectedError() = _UnexpectedError;
const factory GameFailure.dynamicErrorMessage(String erroMessage) =
_DynamicErrorMessage;
}

11
lib/domain/game/game.dart Normal file
View File

@ -0,0 +1,11 @@
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../common/api/api_failure.dart';
part 'game.freezed.dart';
part 'entity/game_entity.dart';
part 'entity/game_prize_entity.dart';
part 'failures/game_failure.dart';
part 'repositories/i_game_repository.dart';

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,7 @@
part of '../game.dart';
abstract class IGameRepository {
Future<Either<GameFailure, List<GamePrize>>> gamePrizeByGameId({
required String id,
});
}

View File

@ -0,0 +1,50 @@
import 'dart:developer';
import 'package:data_channel/data_channel.dart';
import 'package:injectable/injectable.dart';
import '../../../common/api/api_client.dart';
import '../../../common/api/api_failure.dart';
import '../../../common/function/app_function.dart';
import '../../../common/url/api_path.dart';
import '../../../domain/game/game.dart';
import '../game_dtos.dart';
@injectable
class GameRemoteDataProvider {
final ApiClient _apiClient;
final String _logName = "GameRemoteDataProvider";
GameRemoteDataProvider(this._apiClient);
Future<DC<GameFailure, List<GamePrizeDto>>> gamePrizeByGameId({
required String id,
}) async {
try {
final response = await _apiClient.get(
'${ApiPath.gamePrizeByGameId}/$id',
headers: getAuthorizationHeader(),
);
if (response.data['code'] == 401) {
return DC.error(
GameFailure.serverError(ApiFailure.unauthorized('Session Expired')),
);
}
if (response.data['status'] == false) {
return DC.error(
GameFailure.dynamicErrorMessage('Terjadi kesalahan coba lagi nanti'),
);
}
final dto = (response.data['data'] as List)
.map((e) => GamePrizeDto.fromJson(e))
.toList();
return DC.data(dto);
} on ApiFailure catch (e, s) {
log('gamePrizeByGameId', name: _logName, error: e, stackTrace: s);
return DC.error(GameFailure.serverError(e));
}
}
}

View File

@ -0,0 +1,29 @@
part of '../game_dtos.dart';
@freezed
class GameDto with _$GameDto {
const factory GameDto({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
}) = _GameDto;
factory GameDto.fromJson(Map<String, dynamic> json) =>
_$GameDtoFromJson(json);
const GameDto._();
Game toDomain() => Game(
id: id ?? '',
name: name ?? '',
type: type ?? '',
isActive: isActive ?? false,
metadata: metadata ?? const {},
createdAt: createdAt ?? '',
updatedAt: updatedAt ?? '',
);
}

View File

@ -0,0 +1,38 @@
part of '../game_dtos.dart';
@freezed
class GamePrizeDto with _$GamePrizeDto {
const factory GamePrizeDto({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
}) = _GamePrizeDto;
factory GamePrizeDto.fromJson(Map<String, dynamic> json) =>
_$GamePrizeDtoFromJson(json);
const GamePrizeDto._();
/// mapping ke domain
GamePrize toDomain() => GamePrize(
id: id ?? '',
gameId: gameId ?? '',
name: name ?? '',
weight: weight ?? 0,
stock: stock ?? 0,
maxStock: maxStock ?? 0,
threshold: threshold ?? 0,
metadata: metadata ?? const {},
game: game?.toDomain() ?? Game.empty(),
createdAt: createdAt ?? '',
updatedAt: updatedAt ?? '',
);
}

View File

@ -0,0 +1,9 @@
import 'package:freezed_annotation/freezed_annotation.dart';
import '../../domain/game/game.dart';
part 'game_dtos.freezed.dart';
part 'game_dtos.g.dart';
part 'dto/game_dto.dart';
part 'dto/game_prize_dto.dart';

View File

@ -0,0 +1,779 @@
// 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 'game_dtos.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
T _$identity<T>(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',
);
GameDto _$GameDtoFromJson(Map<String, dynamic> json) {
return _GameDto.fromJson(json);
}
/// @nodoc
mixin _$GameDto {
@JsonKey(name: 'id')
String? get id => throw _privateConstructorUsedError;
@JsonKey(name: 'name')
String? get name => throw _privateConstructorUsedError;
@JsonKey(name: 'type')
String? get type => throw _privateConstructorUsedError;
@JsonKey(name: 'is_active')
bool? get isActive => throw _privateConstructorUsedError;
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
String? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at')
String? get updatedAt => throw _privateConstructorUsedError;
/// Serializes this GameDto to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of GameDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GameDtoCopyWith<GameDto> get copyWith => throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GameDtoCopyWith<$Res> {
factory $GameDtoCopyWith(GameDto value, $Res Function(GameDto) then) =
_$GameDtoCopyWithImpl<$Res, GameDto>;
@useResult
$Res call({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
}
/// @nodoc
class _$GameDtoCopyWithImpl<$Res, $Val extends GameDto>
implements $GameDtoCopyWith<$Res> {
_$GameDtoCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GameDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? name = freezed,
Object? type = freezed,
Object? isActive = freezed,
Object? metadata = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
isActive: freezed == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool?,
metadata: freezed == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
)
as $Val,
);
}
}
/// @nodoc
abstract class _$$GameDtoImplCopyWith<$Res> implements $GameDtoCopyWith<$Res> {
factory _$$GameDtoImplCopyWith(
_$GameDtoImpl value,
$Res Function(_$GameDtoImpl) then,
) = __$$GameDtoImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'type') String? type,
@JsonKey(name: 'is_active') bool? isActive,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
}
/// @nodoc
class __$$GameDtoImplCopyWithImpl<$Res>
extends _$GameDtoCopyWithImpl<$Res, _$GameDtoImpl>
implements _$$GameDtoImplCopyWith<$Res> {
__$$GameDtoImplCopyWithImpl(
_$GameDtoImpl _value,
$Res Function(_$GameDtoImpl) _then,
) : super(_value, _then);
/// Create a copy of GameDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? name = freezed,
Object? type = freezed,
Object? isActive = freezed,
Object? metadata = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_$GameDtoImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
type: freezed == type
? _value.type
: type // ignore: cast_nullable_to_non_nullable
as String?,
isActive: freezed == isActive
? _value.isActive
: isActive // ignore: cast_nullable_to_non_nullable
as bool?,
metadata: freezed == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
),
);
}
}
/// @nodoc
@JsonSerializable()
class _$GameDtoImpl extends _GameDto {
const _$GameDtoImpl({
@JsonKey(name: 'id') this.id,
@JsonKey(name: 'name') this.name,
@JsonKey(name: 'type') this.type,
@JsonKey(name: 'is_active') this.isActive,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'created_at') this.createdAt,
@JsonKey(name: 'updated_at') this.updatedAt,
}) : _metadata = metadata,
super._();
factory _$GameDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$GameDtoImplFromJson(json);
@override
@JsonKey(name: 'id')
final String? id;
@override
@JsonKey(name: 'name')
final String? name;
@override
@JsonKey(name: 'type')
final String? type;
@override
@JsonKey(name: 'is_active')
final bool? isActive;
final Map<String, dynamic>? _metadata;
@override
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata {
final value = _metadata;
if (value == null) return null;
if (_metadata is EqualUnmodifiableMapView) return _metadata;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
@override
@JsonKey(name: 'created_at')
final String? createdAt;
@override
@JsonKey(name: 'updated_at')
final String? updatedAt;
@override
String toString() {
return 'GameDto(id: $id, name: $name, type: $type, isActive: $isActive, metadata: $metadata, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GameDtoImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.type, type) || other.type == type) &&
(identical(other.isActive, isActive) ||
other.isActive == isActive) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
id,
name,
type,
isActive,
const DeepCollectionEquality().hash(_metadata),
createdAt,
updatedAt,
);
/// Create a copy of GameDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GameDtoImplCopyWith<_$GameDtoImpl> get copyWith =>
__$$GameDtoImplCopyWithImpl<_$GameDtoImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$GameDtoImplToJson(this);
}
}
abstract class _GameDto extends GameDto {
const factory _GameDto({
@JsonKey(name: 'id') final String? id,
@JsonKey(name: 'name') final String? name,
@JsonKey(name: 'type') final String? type,
@JsonKey(name: 'is_active') final bool? isActive,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'created_at') final String? createdAt,
@JsonKey(name: 'updated_at') final String? updatedAt,
}) = _$GameDtoImpl;
const _GameDto._() : super._();
factory _GameDto.fromJson(Map<String, dynamic> json) = _$GameDtoImpl.fromJson;
@override
@JsonKey(name: 'id')
String? get id;
@override
@JsonKey(name: 'name')
String? get name;
@override
@JsonKey(name: 'type')
String? get type;
@override
@JsonKey(name: 'is_active')
bool? get isActive;
@override
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata;
@override
@JsonKey(name: 'created_at')
String? get createdAt;
@override
@JsonKey(name: 'updated_at')
String? get updatedAt;
/// Create a copy of GameDto
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GameDtoImplCopyWith<_$GameDtoImpl> get copyWith =>
throw _privateConstructorUsedError;
}
GamePrizeDto _$GamePrizeDtoFromJson(Map<String, dynamic> json) {
return _GamePrizeDto.fromJson(json);
}
/// @nodoc
mixin _$GamePrizeDto {
@JsonKey(name: 'id')
String? get id => throw _privateConstructorUsedError;
@JsonKey(name: 'game_id')
String? get gameId => throw _privateConstructorUsedError;
@JsonKey(name: 'name')
String? get name => throw _privateConstructorUsedError;
@JsonKey(name: 'weight')
int? get weight => throw _privateConstructorUsedError;
@JsonKey(name: 'stock')
int? get stock => throw _privateConstructorUsedError;
@JsonKey(name: 'max_stock')
int? get maxStock => throw _privateConstructorUsedError;
@JsonKey(name: 'threshold')
int? get threshold => throw _privateConstructorUsedError;
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata => throw _privateConstructorUsedError;
@JsonKey(name: 'game')
GameDto? get game => throw _privateConstructorUsedError;
@JsonKey(name: 'created_at')
String? get createdAt => throw _privateConstructorUsedError;
@JsonKey(name: 'updated_at')
String? get updatedAt => throw _privateConstructorUsedError;
/// Serializes this GamePrizeDto to a JSON map.
Map<String, dynamic> toJson() => throw _privateConstructorUsedError;
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
$GamePrizeDtoCopyWith<GamePrizeDto> get copyWith =>
throw _privateConstructorUsedError;
}
/// @nodoc
abstract class $GamePrizeDtoCopyWith<$Res> {
factory $GamePrizeDtoCopyWith(
GamePrizeDto value,
$Res Function(GamePrizeDto) then,
) = _$GamePrizeDtoCopyWithImpl<$Res, GamePrizeDto>;
@useResult
$Res call({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
$GameDtoCopyWith<$Res>? get game;
}
/// @nodoc
class _$GamePrizeDtoCopyWithImpl<$Res, $Val extends GamePrizeDto>
implements $GamePrizeDtoCopyWith<$Res> {
_$GamePrizeDtoCopyWithImpl(this._value, this._then);
// ignore: unused_field
final $Val _value;
// ignore: unused_field
final $Res Function($Val) _then;
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? gameId = freezed,
Object? name = freezed,
Object? weight = freezed,
Object? stock = freezed,
Object? maxStock = freezed,
Object? threshold = freezed,
Object? metadata = freezed,
Object? game = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_value.copyWith(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
gameId: freezed == gameId
? _value.gameId
: gameId // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
weight: freezed == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int?,
stock: freezed == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int?,
maxStock: freezed == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int?,
threshold: freezed == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int?,
metadata: freezed == metadata
? _value.metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
game: freezed == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as GameDto?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
)
as $Val,
);
}
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$GameDtoCopyWith<$Res>? get game {
if (_value.game == null) {
return null;
}
return $GameDtoCopyWith<$Res>(_value.game!, (value) {
return _then(_value.copyWith(game: value) as $Val);
});
}
}
/// @nodoc
abstract class _$$GamePrizeDtoImplCopyWith<$Res>
implements $GamePrizeDtoCopyWith<$Res> {
factory _$$GamePrizeDtoImplCopyWith(
_$GamePrizeDtoImpl value,
$Res Function(_$GamePrizeDtoImpl) then,
) = __$$GamePrizeDtoImplCopyWithImpl<$Res>;
@override
@useResult
$Res call({
@JsonKey(name: 'id') String? id,
@JsonKey(name: 'game_id') String? gameId,
@JsonKey(name: 'name') String? name,
@JsonKey(name: 'weight') int? weight,
@JsonKey(name: 'stock') int? stock,
@JsonKey(name: 'max_stock') int? maxStock,
@JsonKey(name: 'threshold') int? threshold,
@JsonKey(name: 'metadata') Map<String, dynamic>? metadata,
@JsonKey(name: 'game') GameDto? game,
@JsonKey(name: 'created_at') String? createdAt,
@JsonKey(name: 'updated_at') String? updatedAt,
});
@override
$GameDtoCopyWith<$Res>? get game;
}
/// @nodoc
class __$$GamePrizeDtoImplCopyWithImpl<$Res>
extends _$GamePrizeDtoCopyWithImpl<$Res, _$GamePrizeDtoImpl>
implements _$$GamePrizeDtoImplCopyWith<$Res> {
__$$GamePrizeDtoImplCopyWithImpl(
_$GamePrizeDtoImpl _value,
$Res Function(_$GamePrizeDtoImpl) _then,
) : super(_value, _then);
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline')
@override
$Res call({
Object? id = freezed,
Object? gameId = freezed,
Object? name = freezed,
Object? weight = freezed,
Object? stock = freezed,
Object? maxStock = freezed,
Object? threshold = freezed,
Object? metadata = freezed,
Object? game = freezed,
Object? createdAt = freezed,
Object? updatedAt = freezed,
}) {
return _then(
_$GamePrizeDtoImpl(
id: freezed == id
? _value.id
: id // ignore: cast_nullable_to_non_nullable
as String?,
gameId: freezed == gameId
? _value.gameId
: gameId // ignore: cast_nullable_to_non_nullable
as String?,
name: freezed == name
? _value.name
: name // ignore: cast_nullable_to_non_nullable
as String?,
weight: freezed == weight
? _value.weight
: weight // ignore: cast_nullable_to_non_nullable
as int?,
stock: freezed == stock
? _value.stock
: stock // ignore: cast_nullable_to_non_nullable
as int?,
maxStock: freezed == maxStock
? _value.maxStock
: maxStock // ignore: cast_nullable_to_non_nullable
as int?,
threshold: freezed == threshold
? _value.threshold
: threshold // ignore: cast_nullable_to_non_nullable
as int?,
metadata: freezed == metadata
? _value._metadata
: metadata // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,
game: freezed == game
? _value.game
: game // ignore: cast_nullable_to_non_nullable
as GameDto?,
createdAt: freezed == createdAt
? _value.createdAt
: createdAt // ignore: cast_nullable_to_non_nullable
as String?,
updatedAt: freezed == updatedAt
? _value.updatedAt
: updatedAt // ignore: cast_nullable_to_non_nullable
as String?,
),
);
}
}
/// @nodoc
@JsonSerializable()
class _$GamePrizeDtoImpl extends _GamePrizeDto {
const _$GamePrizeDtoImpl({
@JsonKey(name: 'id') this.id,
@JsonKey(name: 'game_id') this.gameId,
@JsonKey(name: 'name') this.name,
@JsonKey(name: 'weight') this.weight,
@JsonKey(name: 'stock') this.stock,
@JsonKey(name: 'max_stock') this.maxStock,
@JsonKey(name: 'threshold') this.threshold,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'game') this.game,
@JsonKey(name: 'created_at') this.createdAt,
@JsonKey(name: 'updated_at') this.updatedAt,
}) : _metadata = metadata,
super._();
factory _$GamePrizeDtoImpl.fromJson(Map<String, dynamic> json) =>
_$$GamePrizeDtoImplFromJson(json);
@override
@JsonKey(name: 'id')
final String? id;
@override
@JsonKey(name: 'game_id')
final String? gameId;
@override
@JsonKey(name: 'name')
final String? name;
@override
@JsonKey(name: 'weight')
final int? weight;
@override
@JsonKey(name: 'stock')
final int? stock;
@override
@JsonKey(name: 'max_stock')
final int? maxStock;
@override
@JsonKey(name: 'threshold')
final int? threshold;
final Map<String, dynamic>? _metadata;
@override
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata {
final value = _metadata;
if (value == null) return null;
if (_metadata is EqualUnmodifiableMapView) return _metadata;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(value);
}
@override
@JsonKey(name: 'game')
final GameDto? game;
@override
@JsonKey(name: 'created_at')
final String? createdAt;
@override
@JsonKey(name: 'updated_at')
final String? updatedAt;
@override
String toString() {
return 'GamePrizeDto(id: $id, gameId: $gameId, name: $name, weight: $weight, stock: $stock, maxStock: $maxStock, threshold: $threshold, metadata: $metadata, game: $game, createdAt: $createdAt, updatedAt: $updatedAt)';
}
@override
bool operator ==(Object other) {
return identical(this, other) ||
(other.runtimeType == runtimeType &&
other is _$GamePrizeDtoImpl &&
(identical(other.id, id) || other.id == id) &&
(identical(other.gameId, gameId) || other.gameId == gameId) &&
(identical(other.name, name) || other.name == name) &&
(identical(other.weight, weight) || other.weight == weight) &&
(identical(other.stock, stock) || other.stock == stock) &&
(identical(other.maxStock, maxStock) ||
other.maxStock == maxStock) &&
(identical(other.threshold, threshold) ||
other.threshold == threshold) &&
const DeepCollectionEquality().equals(other._metadata, _metadata) &&
(identical(other.game, game) || other.game == game) &&
(identical(other.createdAt, createdAt) ||
other.createdAt == createdAt) &&
(identical(other.updatedAt, updatedAt) ||
other.updatedAt == updatedAt));
}
@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(
runtimeType,
id,
gameId,
name,
weight,
stock,
maxStock,
threshold,
const DeepCollectionEquality().hash(_metadata),
game,
createdAt,
updatedAt,
);
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@override
@pragma('vm:prefer-inline')
_$$GamePrizeDtoImplCopyWith<_$GamePrizeDtoImpl> get copyWith =>
__$$GamePrizeDtoImplCopyWithImpl<_$GamePrizeDtoImpl>(this, _$identity);
@override
Map<String, dynamic> toJson() {
return _$$GamePrizeDtoImplToJson(this);
}
}
abstract class _GamePrizeDto extends GamePrizeDto {
const factory _GamePrizeDto({
@JsonKey(name: 'id') final String? id,
@JsonKey(name: 'game_id') final String? gameId,
@JsonKey(name: 'name') final String? name,
@JsonKey(name: 'weight') final int? weight,
@JsonKey(name: 'stock') final int? stock,
@JsonKey(name: 'max_stock') final int? maxStock,
@JsonKey(name: 'threshold') final int? threshold,
@JsonKey(name: 'metadata') final Map<String, dynamic>? metadata,
@JsonKey(name: 'game') final GameDto? game,
@JsonKey(name: 'created_at') final String? createdAt,
@JsonKey(name: 'updated_at') final String? updatedAt,
}) = _$GamePrizeDtoImpl;
const _GamePrizeDto._() : super._();
factory _GamePrizeDto.fromJson(Map<String, dynamic> json) =
_$GamePrizeDtoImpl.fromJson;
@override
@JsonKey(name: 'id')
String? get id;
@override
@JsonKey(name: 'game_id')
String? get gameId;
@override
@JsonKey(name: 'name')
String? get name;
@override
@JsonKey(name: 'weight')
int? get weight;
@override
@JsonKey(name: 'stock')
int? get stock;
@override
@JsonKey(name: 'max_stock')
int? get maxStock;
@override
@JsonKey(name: 'threshold')
int? get threshold;
@override
@JsonKey(name: 'metadata')
Map<String, dynamic>? get metadata;
@override
@JsonKey(name: 'game')
GameDto? get game;
@override
@JsonKey(name: 'created_at')
String? get createdAt;
@override
@JsonKey(name: 'updated_at')
String? get updatedAt;
/// Create a copy of GamePrizeDto
/// with the given fields replaced by the non-null parameter values.
@override
@JsonKey(includeFromJson: false, includeToJson: false)
_$$GamePrizeDtoImplCopyWith<_$GamePrizeDtoImpl> get copyWith =>
throw _privateConstructorUsedError;
}

View File

@ -0,0 +1,61 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'game_dtos.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
_$GameDtoImpl _$$GameDtoImplFromJson(Map<String, dynamic> json) =>
_$GameDtoImpl(
id: json['id'] as String?,
name: json['name'] as String?,
type: json['type'] as String?,
isActive: json['is_active'] as bool?,
metadata: json['metadata'] as Map<String, dynamic>?,
createdAt: json['created_at'] as String?,
updatedAt: json['updated_at'] as String?,
);
Map<String, dynamic> _$$GameDtoImplToJson(_$GameDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'name': instance.name,
'type': instance.type,
'is_active': instance.isActive,
'metadata': instance.metadata,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
};
_$GamePrizeDtoImpl _$$GamePrizeDtoImplFromJson(Map<String, dynamic> json) =>
_$GamePrizeDtoImpl(
id: json['id'] as String?,
gameId: json['game_id'] as String?,
name: json['name'] as String?,
weight: (json['weight'] as num?)?.toInt(),
stock: (json['stock'] as num?)?.toInt(),
maxStock: (json['max_stock'] as num?)?.toInt(),
threshold: (json['threshold'] as num?)?.toInt(),
metadata: json['metadata'] as Map<String, dynamic>?,
game: json['game'] == null
? null
: GameDto.fromJson(json['game'] as Map<String, dynamic>),
createdAt: json['created_at'] as String?,
updatedAt: json['updated_at'] as String?,
);
Map<String, dynamic> _$$GamePrizeDtoImplToJson(_$GamePrizeDtoImpl instance) =>
<String, dynamic>{
'id': instance.id,
'game_id': instance.gameId,
'name': instance.name,
'weight': instance.weight,
'stock': instance.stock,
'max_stock': instance.maxStock,
'threshold': instance.threshold,
'metadata': instance.metadata,
'game': instance.game,
'created_at': instance.createdAt,
'updated_at': instance.updatedAt,
};

View File

@ -0,0 +1,36 @@
import 'dart:developer';
import 'package:dartz/dartz.dart';
import 'package:injectable/injectable.dart';
import '../../../domain/game/game.dart';
import '../datasources/remote_data_provider.dart';
@Injectable(as: IGameRepository)
class GameRepository implements IGameRepository {
final GameRemoteDataProvider _remoteDataProvider;
final String _logName = 'GameRepository';
GameRepository(this._remoteDataProvider);
@override
Future<Either<GameFailure, List<GamePrize>>> gamePrizeByGameId({
required String id,
}) async {
try {
final result = await _remoteDataProvider.gamePrizeByGameId(id: id);
if (result.hasError) {
return left(result.error!);
}
final data = result.data!.map((e) => e.toDomain()).toList();
return right(data);
} catch (e, s) {
log('gamePrizeByGameId', name: _logName, error: e, stackTrace: s);
return left(const GameFailure.unexpectedError());
}
}
}

View File

@ -16,6 +16,8 @@ import 'package:enaklo/application/auth/check_phone_form/check_phone_form_bloc.d
as _i869; as _i869;
import 'package:enaklo/application/auth/login_form/login_form_bloc.dart' import 'package:enaklo/application/auth/login_form/login_form_bloc.dart'
as _i510; as _i510;
import 'package:enaklo/application/auth/logout_form/logout_form_bloc.dart'
as _i216;
import 'package:enaklo/application/auth/register_form/register_form_bloc.dart' import 'package:enaklo/application/auth/register_form/register_form_bloc.dart'
as _i260; as _i260;
import 'package:enaklo/application/auth/resend_form/resend_form_bloc.dart' import 'package:enaklo/application/auth/resend_form/resend_form_bloc.dart'
@ -24,6 +26,8 @@ import 'package:enaklo/application/auth/set_password/set_password_form_bloc.dart
as _i174; as _i174;
import 'package:enaklo/application/auth/verify_form/verify_form_bloc.dart' import 'package:enaklo/application/auth/verify_form/verify_form_bloc.dart'
as _i521; as _i521;
import 'package:enaklo/application/game/game_price_loader/game_prize_loader_bloc.dart'
as _i925;
import 'package:enaklo/common/api/api_client.dart' as _i842; import 'package:enaklo/common/api/api_client.dart' as _i842;
import 'package:enaklo/common/di/di_auto_route.dart' as _i619; import 'package:enaklo/common/di/di_auto_route.dart' as _i619;
import 'package:enaklo/common/di/di_connectivity.dart' as _i644; import 'package:enaklo/common/di/di_connectivity.dart' as _i644;
@ -31,6 +35,7 @@ import 'package:enaklo/common/di/di_dio.dart' as _i842;
import 'package:enaklo/common/di/di_shared_preferences.dart' as _i672; import 'package:enaklo/common/di/di_shared_preferences.dart' as _i672;
import 'package:enaklo/common/network/network_client.dart' as _i109; import 'package:enaklo/common/network/network_client.dart' as _i109;
import 'package:enaklo/domain/auth/auth.dart' as _i995; import 'package:enaklo/domain/auth/auth.dart' as _i995;
import 'package:enaklo/domain/game/game.dart' as _i96;
import 'package:enaklo/env.dart' as _i372; import 'package:enaklo/env.dart' as _i372;
import 'package:enaklo/infrastructure/auth/datasources/local_data_provider.dart' import 'package:enaklo/infrastructure/auth/datasources/local_data_provider.dart'
as _i1003; as _i1003;
@ -38,6 +43,10 @@ import 'package:enaklo/infrastructure/auth/datasources/remote_data_provider.dart
as _i818; as _i818;
import 'package:enaklo/infrastructure/auth/repositories/auth_repository.dart' import 'package:enaklo/infrastructure/auth/repositories/auth_repository.dart'
as _i879; as _i879;
import 'package:enaklo/infrastructure/game/datasources/remote_data_provider.dart'
as _i143;
import 'package:enaklo/infrastructure/game/repositories/game_repository.dart'
as _i547;
import 'package:enaklo/presentation/router/app_router.dart' as _i698; import 'package:enaklo/presentation/router/app_router.dart' as _i698;
import 'package:get_it/get_it.dart' as _i174; import 'package:get_it/get_it.dart' as _i174;
import 'package:injectable/injectable.dart' as _i526; import 'package:injectable/injectable.dart' as _i526;
@ -78,6 +87,15 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i818.AuthRemoteDataProvider>( gh.factory<_i818.AuthRemoteDataProvider>(
() => _i818.AuthRemoteDataProvider(gh<_i842.ApiClient>()), () => _i818.AuthRemoteDataProvider(gh<_i842.ApiClient>()),
); );
gh.factory<_i143.GameRemoteDataProvider>(
() => _i143.GameRemoteDataProvider(gh<_i842.ApiClient>()),
);
gh.factory<_i96.IGameRepository>(
() => _i547.GameRepository(gh<_i143.GameRemoteDataProvider>()),
);
gh.factory<_i925.GamePrizeLoaderBloc>(
() => _i925.GamePrizeLoaderBloc(gh<_i96.IGameRepository>()),
);
gh.factory<_i995.IAuthRepository>( gh.factory<_i995.IAuthRepository>(
() => _i879.AuthRepository( () => _i879.AuthRepository(
gh<_i818.AuthRemoteDataProvider>(), gh<_i818.AuthRemoteDataProvider>(),
@ -102,6 +120,9 @@ extension GetItInjectableX on _i174.GetIt {
gh.factory<_i771.AuthBloc>( gh.factory<_i771.AuthBloc>(
() => _i771.AuthBloc(gh<_i995.IAuthRepository>()), () => _i771.AuthBloc(gh<_i995.IAuthRepository>()),
); );
gh.factory<_i216.LogoutFormBloc>(
() => _i216.LogoutFormBloc(gh<_i995.IAuthRepository>()),
);
gh.factory<_i510.LoginFormBloc>( gh.factory<_i510.LoginFormBloc>(
() => _i510.LoginFormBloc(gh<_i995.IAuthRepository>()), () => _i510.LoginFormBloc(gh<_i995.IAuthRepository>()),
); );

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import '../application/auth/auth_bloc.dart'; import '../application/auth/auth_bloc.dart';
import '../application/auth/logout_form/logout_form_bloc.dart';
import '../common/theme/theme.dart'; import '../common/theme/theme.dart';
import '../common/constant/app_constant.dart'; import '../common/constant/app_constant.dart';
import '../injection.dart'; import '../injection.dart';
@ -21,7 +22,10 @@ class _AppWidgetState extends State<AppWidget> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MultiBlocProvider( return MultiBlocProvider(
providers: [BlocProvider(create: (context) => getIt<AuthBloc>())], providers: [
BlocProvider(create: (context) => getIt<AuthBloc>()),
BlocProvider(create: (context) => getIt<LogoutFormBloc>()),
],
child: MaterialApp.router( child: MaterialApp.router(
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
title: AppConstant.appName, title: AppConstant.appName,

View File

@ -1,341 +1,368 @@
import 'package:auto_route/auto_route.dart'; import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import '../../../../../application/auth/logout_form/logout_form_bloc.dart';
import '../../../../../common/theme/theme.dart'; import '../../../../../common/theme/theme.dart';
import '../../../../../injection.dart';
import '../../../../components/toast/flushbar.dart';
import '../../../../router/app_router.gr.dart'; import '../../../../router/app_router.gr.dart';
@RoutePage() @RoutePage()
class ProfilePage extends StatelessWidget { class ProfilePage extends StatelessWidget implements AutoRouteWrapper {
const ProfilePage({super.key}); const ProfilePage({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return BlocListener<LogoutFormBloc, LogoutFormState>(
backgroundColor: AppColor.background, listener: (context, state) {
appBar: AppBar(title: Text('Profil'), automaticallyImplyLeading: false), state.failureOrAuthOption.fold(
body: SingleChildScrollView( () => null,
child: Column( (either) => either.fold(
children: [ (f) => AppFlushbar.showAuthFailureToast(context, f),
// Profile Header (_) => context.router.replaceAll([LoginRoute()]),
Container( ),
width: double.infinity, );
color: AppColor.white, },
padding: const EdgeInsets.all(20),
child: Column( child: Scaffold(
children: [ backgroundColor: AppColor.background,
// Profile Avatar & Info appBar: AppBar(title: Text('Profil'), automaticallyImplyLeading: false),
GestureDetector( body: SingleChildScrollView(
onTap: () => context.router.push(AccountMyRoute()), child: Column(
child: Container( children: [
decoration: BoxDecoration( // Profile Header
gradient: LinearGradient( Container(
colors: AppColor.primaryGradient, width: double.infinity,
begin: Alignment.topLeft, color: AppColor.white,
end: Alignment.bottomRight, padding: const EdgeInsets.all(20),
child: Column(
children: [
// Profile Avatar & Info
GestureDetector(
onTap: () => context.router.push(AccountMyRoute()),
child: Container(
decoration: BoxDecoration(
gradient: LinearGradient(
colors: AppColor.primaryGradient,
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
borderRadius: BorderRadius.circular(16),
), ),
borderRadius: BorderRadius.circular(16), child: Stack(
), children: [
child: Stack( // Background Pattern
children: [ Positioned(
// Background Pattern top: -20,
Positioned( right: -20,
top: -20,
right: -20,
child: Container(
width: 80,
height: 80,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.white.withOpacity(0.1),
),
),
),
Positioned(
top: 30,
right: 20,
child: Container(
width: 40,
height: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.white.withOpacity(0.08),
),
),
),
Positioned(
bottom: -10,
left: -10,
child: Container(
width: 60,
height: 60,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: AppColor.white.withOpacity(0.06),
),
),
),
// Decorative Lines
Positioned(
top: 10,
left: -5,
child: Transform.rotate(
angle: 0.5,
child: Container( child: Container(
width: 30, width: 80,
height: 2, height: 80,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.15), shape: BoxShape.circle,
borderRadius: BorderRadius.circular(1), color: AppColor.white.withOpacity(0.1),
), ),
), ),
), ),
), Positioned(
Positioned( top: 30,
bottom: 15, right: 20,
right: 10,
child: Transform.rotate(
angle: -0.5,
child: Container( child: Container(
width: 25, width: 40,
height: 2, height: 40,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.white.withOpacity(0.15), shape: BoxShape.circle,
borderRadius: BorderRadius.circular(1), color: AppColor.white.withOpacity(0.08),
), ),
), ),
), ),
), Positioned(
// Main Content bottom: -10,
Padding( left: -10,
padding: const EdgeInsets.all(16.0), child: Container(
child: Row( width: 60,
children: [ height: 60,
// Avatar decoration: BoxDecoration(
Container( shape: BoxShape.circle,
width: 60, color: AppColor.white.withOpacity(0.06),
height: 60, ),
),
),
// Decorative Lines
Positioned(
top: 10,
left: -5,
child: Transform.rotate(
angle: 0.5,
child: Container(
width: 30,
height: 2,
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColor.white, color: AppColor.white.withOpacity(0.15),
shape: BoxShape.circle, borderRadius: BorderRadius.circular(1),
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(0.1),
blurRadius: 8,
offset: const Offset(0, 2),
),
],
),
child: Icon(
Icons.person,
size: 30,
color: AppColor.primary,
), ),
), ),
const SizedBox(width: 16), ),
// User Info ),
Expanded( Positioned(
child: Column( bottom: 15,
crossAxisAlignment: right: 10,
CrossAxisAlignment.start, child: Transform.rotate(
children: [ angle: -0.5,
Text( child: Container(
'EFRIL', width: 25,
style: AppStyle.lg.copyWith( height: 2,
fontWeight: FontWeight.bold, decoration: BoxDecoration(
color: AppColor.white, color: AppColor.white.withOpacity(0.15),
letterSpacing: 0.5, borderRadius: BorderRadius.circular(1),
),
),
),
),
// Main Content
Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
children: [
// Avatar
Container(
width: 60,
height: 60,
decoration: BoxDecoration(
color: AppColor.white,
shape: BoxShape.circle,
boxShadow: [
BoxShadow(
color: AppColor.black.withOpacity(
0.1,
),
blurRadius: 8,
offset: const Offset(0, 2),
), ),
), ],
const SizedBox(height: 4), ),
Text( child: Icon(
'+6283873987851', Icons.person,
style: AppStyle.sm.copyWith( size: 30,
color: AppColor.white.withOpacity( color: AppColor.primary,
0.9, ),
),
const SizedBox(width: 16),
// User Info
Expanded(
child: Column(
crossAxisAlignment:
CrossAxisAlignment.start,
children: [
Text(
'EFRIL',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.bold,
color: AppColor.white,
letterSpacing: 0.5,
), ),
), ),
), const SizedBox(height: 4),
], Text(
'+6283873987851',
style: AppStyle.sm.copyWith(
color: AppColor.white.withOpacity(
0.9,
),
),
),
],
),
),
// Arrow Icon
Icon(
Icons.arrow_forward_ios,
color: AppColor.white,
size: 14,
),
],
),
),
],
),
),
),
],
),
),
const SizedBox(height: 8),
// Menu Items
Container(
color: AppColor.white,
child: Column(
children: [
_buildMenuItem(
icon: Icons.location_on_outlined,
title: 'Alamat Tersimpan',
onTap: () => context.router.push(AddressRoute()),
),
_buildMenuItem(
icon: Icons.payment_outlined,
title: 'Pembayaran',
onTap: () => context.router.push(PaymentRoute()),
),
_buildMenuItem(
icon: Icons.help_outline,
title: 'Pusat Bantuan',
onTap: () {},
),
_buildMenuItem(
icon: Icons.settings_outlined,
title: 'Pengaturan',
onTap: () {},
showDivider: false,
),
],
),
),
const SizedBox(height: 8),
// Legal & Privacy Section
Container(
color: AppColor.white,
child: Column(
children: [
_buildMenuItem(
icon: Icons.description_outlined,
title: 'Syarat dan Ketentuan',
onTap: () {},
),
_buildMenuItem(
icon: Icons.privacy_tip_outlined,
title: 'Kebijakan Privasi',
onTap: () {},
showDivider: false,
),
],
),
),
const SizedBox(height: 8),
// Customer Service Section
Container(
color: AppColor.white,
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Butuh Bantuan?',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'Customer Service kami siap untuk membantu',
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
const SizedBox(height: 16),
// WhatsApp Customer Service
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.backgroundLight,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.chat,
color: AppColor.white,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Enaklo Customer Service (chat only)',
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
), ),
), ),
// Arrow Icon Text(
Icon( '0812-1111-8456',
Icons.arrow_forward_ios, style: AppStyle.md.copyWith(
color: AppColor.white, fontWeight: FontWeight.w600,
size: 14, color: AppColor.success,
),
), ),
], ],
), ),
), ),
Icon(
Icons.arrow_forward_ios,
color: AppColor.textSecondary,
size: 14,
),
], ],
), ),
), ),
), ],
], ),
), ),
),
const SizedBox(height: 8), const SizedBox(height: 20),
// Menu Items // Footer Section
Container( Container(
color: AppColor.white, color: AppColor.white,
child: Column( padding: const EdgeInsets.all(20),
children: [ child: Row(
_buildMenuItem( mainAxisAlignment: MainAxisAlignment.spaceBetween,
icon: Icons.location_on_outlined, children: [
title: 'Alamat Tersimpan', Text(
onTap: () => context.router.push(AddressRoute()), 'Version 4.6.1',
),
_buildMenuItem(
icon: Icons.payment_outlined,
title: 'Pembayaran',
onTap: () => context.router.push(PaymentRoute()),
),
_buildMenuItem(
icon: Icons.help_outline,
title: 'Pusat Bantuan',
onTap: () {},
),
_buildMenuItem(
icon: Icons.settings_outlined,
title: 'Pengaturan',
onTap: () {},
showDivider: false,
),
],
),
),
const SizedBox(height: 8),
// Legal & Privacy Section
Container(
color: AppColor.white,
child: Column(
children: [
_buildMenuItem(
icon: Icons.description_outlined,
title: 'Syarat dan Ketentuan',
onTap: () {},
),
_buildMenuItem(
icon: Icons.privacy_tip_outlined,
title: 'Kebijakan Privasi',
onTap: () {},
showDivider: false,
),
],
),
),
const SizedBox(height: 8),
// Customer Service Section
Container(
color: AppColor.white,
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Butuh Bantuan?',
style: AppStyle.lg.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.textPrimary,
),
),
const SizedBox(height: 8),
Text(
'Customer Service kami siap untuk membantu',
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
const SizedBox(height: 16),
// WhatsApp Customer Service
Container(
padding: const EdgeInsets.all(16),
decoration: BoxDecoration(
color: AppColor.backgroundLight,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: AppColor.borderLight),
),
child: Row(
children: [
Container(
width: 40,
height: 40,
decoration: BoxDecoration(
color: Colors.green,
borderRadius: BorderRadius.circular(8),
),
child: Icon(
Icons.chat,
color: AppColor.white,
size: 20,
),
),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Enaklo Customer Service (chat only)',
style: AppStyle.sm.copyWith(
color: AppColor.textSecondary,
),
),
Text(
'0812-1111-8456',
style: AppStyle.md.copyWith(
fontWeight: FontWeight.w600,
color: AppColor.success,
),
),
],
),
),
Icon(
Icons.arrow_forward_ios,
color: AppColor.textSecondary,
size: 14,
),
],
),
),
],
),
),
const SizedBox(height: 20),
// Footer Section
Container(
color: AppColor.white,
padding: const EdgeInsets.all(20),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Version 4.6.1',
style: AppStyle.sm.copyWith(color: AppColor.textSecondary),
),
GestureDetector(
onTap: () => _showLogoutDialog(context),
child: Text(
'Logout',
style: AppStyle.sm.copyWith( style: AppStyle.sm.copyWith(
color: AppColor.primary, color: AppColor.textSecondary,
fontWeight: FontWeight.w600,
), ),
), ),
), GestureDetector(
], onTap: () => _showLogoutDialog(
context,
onLogout: () => context.read<LogoutFormBloc>().add(
const LogoutFormEvent.submitted(),
),
),
child: Text(
'Logout',
style: AppStyle.sm.copyWith(
color: AppColor.primary,
fontWeight: FontWeight.w600,
),
),
),
],
),
), ),
),
const SizedBox(height: 100), // Bottom spacing const SizedBox(height: 100), // Bottom spacing
], ],
),
), ),
), ),
); );
@ -372,7 +399,10 @@ class ProfilePage extends StatelessWidget {
); );
} }
void _showLogoutDialog(BuildContext context) { void _showLogoutDialog(
BuildContext context, {
required VoidCallback onLogout,
}) {
showDialog( showDialog(
context: context, context: context,
builder: (BuildContext context) { builder: (BuildContext context) {
@ -395,6 +425,7 @@ class ProfilePage extends StatelessWidget {
onPressed: () => Navigator.of(context).pop(), onPressed: () => Navigator.of(context).pop(),
), ),
TextButton( TextButton(
onPressed: onLogout,
child: Text( child: Text(
'Logout', 'Logout',
style: AppStyle.md.copyWith( style: AppStyle.md.copyWith(
@ -402,15 +433,14 @@ class ProfilePage extends StatelessWidget {
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
), ),
), ),
onPressed: () {
Navigator.of(context).pop();
// Add logout logic here
// Example: context.router.pushAndClearStack('/login');
},
), ),
], ],
); );
}, },
); );
} }
@override
Widget wrappedRoute(BuildContext context) =>
BlocProvider(create: (_) => getIt<LogoutFormBloc>(), child: this);
} }

View File

@ -20,6 +20,7 @@ class PrizeHistory {
final int value; final int value;
final Color color; final Color color;
final IconData icon; final IconData icon;
final String? gamePrizeId; // Added for API integration
PrizeHistory({ PrizeHistory({
required this.prize, required this.prize,
@ -27,5 +28,73 @@ class PrizeHistory {
required this.value, required this.value,
required this.color, required this.color,
required this.icon, required this.icon,
this.gamePrizeId,
}); });
// CopyWith method for updating properties
PrizeHistory copyWith({
String? prize,
DateTime? dateTime,
int? value,
Color? color,
IconData? icon,
String? gamePrizeId,
}) {
return PrizeHistory(
prize: prize ?? this.prize,
dateTime: dateTime ?? this.dateTime,
value: value ?? this.value,
color: color ?? this.color,
icon: icon ?? this.icon,
gamePrizeId: gamePrizeId ?? this.gamePrizeId,
);
}
// Convert to Map for storage/API calls
Map<String, dynamic> toMap() {
return {
'prize': prize,
'dateTime': dateTime.toIso8601String(),
'value': value,
'gamePrizeId': gamePrizeId,
};
}
// Create from Map for loading from storage/API
factory PrizeHistory.fromMap(
Map<String, dynamic> map, {
required Color color,
required IconData icon,
}) {
return PrizeHistory(
prize: map['prize'] ?? '',
dateTime: DateTime.tryParse(map['dateTime'] ?? '') ?? DateTime.now(),
value: map['value'] ?? 0,
color: color,
icon: icon,
gamePrizeId: map['gamePrizeId'],
);
}
// Format date for display
String get formattedDate {
return '${dateTime.day.toString().padLeft(2, '0')}/${dateTime.month.toString().padLeft(2, '0')}/${dateTime.year} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
}
// Get prize category based on value
String get prizeCategory {
if (value >= 1000000) return 'Jackpot';
if (value >= 100000) return 'Big Prize';
if (value >= 10000) return 'Medium Prize';
if (value >= 1000) return 'Small Prize';
return 'Token/Spin';
}
// Get prize category color
Color get categoryColor {
if (value >= 1000000) return const Color(0xFFFFD700); // Gold
if (value >= 100000) return const Color(0xFFC0C0C0); // Silver
if (value >= 10000) return const Color(0xFFCD7F32); // Bronze
return color; // Default color
}
} }

View File

@ -252,7 +252,7 @@ class FerrisWheelRoute extends _i33.PageRouteInfo<void> {
static _i33.PageInfo page = _i33.PageInfo( static _i33.PageInfo page = _i33.PageInfo(
name, name,
builder: (data) { builder: (data) {
return const _i10.FerrisWheelPage(); return _i33.WrappedRoute(child: const _i10.FerrisWheelPage());
}, },
); );
} }