EnakPoint & EnakCoin wallet, PIN, outlet, order, community, reward

- Wallet EnakPoint & EnakCoin: beranda, riwayat, saldo kedaluwarsa, kode
  bayar, tukar EnakCoin, transfer
- PIN (buat, ganti, lupa), outlet, riwayat order, game spin
- Halaman community, webview, coming soon, dan ikon menu beranda baru
- Redesign Reward Saya: kartu tiket, filter chip, detail kode + QR
- Hapus customer point loader, menu, dan checkout lama
- Base URL ke enaklo-pos-api.altru.id

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
efrilm
2026-10-01 01:43:22 +07:00
co-authored by Claude Opus 5.5
parent 5954510b93
commit f5e3075205
253 changed files with 47212 additions and 17437 deletions
@@ -0,0 +1,87 @@
import 'package:bloc/bloc.dart';
import 'package:dartz/dartz.dart';
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:injectable/injectable.dart';
import '../../../common/function/app_function.dart';
import '../../../domain/wallet/wallet.dart';
part 'exchange_form_event.dart';
part 'exchange_form_state.dart';
part 'exchange_form_bloc.freezed.dart';
@injectable
class ExchangeFormBloc extends Bloc<ExchangeFormEvent, ExchangeFormState> {
final IWalletRepository _repository;
ExchangeFormBloc(this._repository) : super(ExchangeFormState.initial()) {
on<ExchangeFormEvent>(_onExchangeFormEvent);
}
Future<void> _onExchangeFormEvent(
ExchangeFormEvent event,
Emitter<ExchangeFormState> emit,
) {
return event.map(
// UI men-debounce ketikan sebelum mengirim event ini.
coinsChanged: (e) async {
emit(
state.copyWith(
coins: e.coins,
preview: null,
previewFailureOption: none(),
idempotencyKey: null,
isPreviewing: e.coins > 0,
),
);
if (e.coins <= 0) return;
final result = await _repository.previewExchange(coins: e.coins);
// Customer sudah mengetik angka lain.
if (state.coins != e.coins) return;
emit(
result
.fold(
(f) => state.copyWith(previewFailureOption: optionOf(f)),
(preview) => state.copyWith(preview: preview),
)
.copyWith(isPreviewing: false),
);
},
submitted: (e) async {
// Key baru untuk setiap konfirmasi, kecuali mengulang request yang
// sebelumnya gagal karena jaringan.
final key = state.idempotencyKey ?? generateUuidV4();
emit(
state.copyWith(
isSubmitting: true,
idempotencyKey: key,
failureOption: none(),
),
);
final result = await _repository.exchange(
coins: state.coins,
pin: e.pin,
idempotencyKey: key,
);
emit(
result
.fold(
(f) => state.copyWith(
failureOption: optionOf(f),
idempotencyKey: f.isNetworkError ? key : null,
),
(exchangeResult) => state.copyWith(
result: exchangeResult,
idempotencyKey: null,
),
)
.copyWith(isSubmitting: false),
);
},
);
}
}
@@ -0,0 +1,650 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
// coverage:ignore-file
// ignore_for_file: type=lint, type=warning, deprecated_member_use, deprecated_member_use_from_same_package
// 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 'exchange_form_bloc.dart';
// **************************************************************************
// FreezedGenerator
// **************************************************************************
// GENERATED CODE - DO NOT MODIFY BY HAND
// dart format off
T _$identity<T>(T value) => value;
/// @nodoc
mixin _$ExchangeFormEvent {
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is ExchangeFormEvent);
}
@override
int get hashCode => runtimeType.hashCode;
@override
String toString() {
return 'ExchangeFormEvent()';
}
}
/// @nodoc
class $ExchangeFormEventCopyWith<$Res> {
$ExchangeFormEventCopyWith(ExchangeFormEvent _, $Res Function(ExchangeFormEvent) __);
}
/// Adds pattern-matching-related methods to [ExchangeFormEvent].
extension ExchangeFormEventPatterns on ExchangeFormEvent {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>({TResult Function( _CoinsChanged value)? coinsChanged,TResult Function( _Submitted value)? submitted,required TResult orElse(),}){
final _that = this;
switch (_that) {
case _CoinsChanged() when coinsChanged != null:
return coinsChanged(_that);case _Submitted() when submitted != null:
return submitted(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>({required TResult Function( _CoinsChanged value) coinsChanged,required TResult Function( _Submitted value) submitted,}){
final _that = this;
switch (_that) {
case _CoinsChanged():
return coinsChanged(_that);case _Submitted():
return submitted(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>({TResult? Function( _CoinsChanged value)? coinsChanged,TResult? Function( _Submitted value)? submitted,}){
final _that = this;
switch (_that) {
case _CoinsChanged() when coinsChanged != null:
return coinsChanged(_that);case _Submitted() when submitted != null:
return submitted(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>({TResult Function( int coins)? coinsChanged,TResult Function( String pin)? submitted,required TResult orElse(),}) {final _that = this;
switch (_that) {
case _CoinsChanged() when coinsChanged != null:
return coinsChanged(_that.coins);case _Submitted() when submitted != null:
return submitted(_that.pin);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>({required TResult Function( int coins) coinsChanged,required TResult Function( String pin) submitted,}) {final _that = this;
switch (_that) {
case _CoinsChanged():
return coinsChanged(_that.coins);case _Submitted():
return submitted(_that.pin);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>({TResult? Function( int coins)? coinsChanged,TResult? Function( String pin)? submitted,}) {final _that = this;
switch (_that) {
case _CoinsChanged() when coinsChanged != null:
return coinsChanged(_that.coins);case _Submitted() when submitted != null:
return submitted(_that.pin);case _:
return null;
}
}
}
/// @nodoc
class _CoinsChanged implements ExchangeFormEvent {
const _CoinsChanged(this.coins);
final int coins;
/// Create a copy of ExchangeFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$CoinsChangedCopyWith<_CoinsChanged> get copyWith => __$CoinsChangedCopyWithImpl<_CoinsChanged>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _CoinsChanged&&(identical(other.coins, coins) || other.coins == coins));
}
@override
int get hashCode {
return Object.hash(runtimeType,coins);
}
@override
String toString() {
return 'ExchangeFormEvent.coinsChanged(coins: $coins)';
}
}
/// @nodoc
abstract mixin class _$CoinsChangedCopyWith<$Res> implements $ExchangeFormEventCopyWith<$Res> {
factory _$CoinsChangedCopyWith(_CoinsChanged value, $Res Function(_CoinsChanged) _then) = __$CoinsChangedCopyWithImpl;
@useResult
$Res call({
int coins
});
}
/// @nodoc
class __$CoinsChangedCopyWithImpl<$Res>
implements _$CoinsChangedCopyWith<$Res> {
__$CoinsChangedCopyWithImpl(this._self, this._then);
final _CoinsChanged _self;
final $Res Function(_CoinsChanged) _then;
/// Create a copy of ExchangeFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? coins = null,}) {
return _then(_CoinsChanged(
null == coins ? _self.coins : coins // ignore: cast_nullable_to_non_nullable
as int,
));
}
}
/// @nodoc
class _Submitted implements ExchangeFormEvent {
const _Submitted(this.pin);
final String pin;
/// Create a copy of ExchangeFormEvent
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$SubmittedCopyWith<_Submitted> get copyWith => __$SubmittedCopyWithImpl<_Submitted>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Submitted&&(identical(other.pin, pin) || other.pin == pin));
}
@override
int get hashCode {
return Object.hash(runtimeType,pin);
}
@override
String toString() {
return 'ExchangeFormEvent.submitted(pin: $pin)';
}
}
/// @nodoc
abstract mixin class _$SubmittedCopyWith<$Res> implements $ExchangeFormEventCopyWith<$Res> {
factory _$SubmittedCopyWith(_Submitted value, $Res Function(_Submitted) _then) = __$SubmittedCopyWithImpl;
@useResult
$Res call({
String pin
});
}
/// @nodoc
class __$SubmittedCopyWithImpl<$Res>
implements _$SubmittedCopyWith<$Res> {
__$SubmittedCopyWithImpl(this._self, this._then);
final _Submitted _self;
final $Res Function(_Submitted) _then;
/// Create a copy of ExchangeFormEvent
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') $Res call({Object? pin = null,}) {
return _then(_Submitted(
null == pin ? _self.pin : pin // ignore: cast_nullable_to_non_nullable
as String,
));
}
}
/// @nodoc
mixin _$ExchangeFormState {
int get coins; ExchangePreview? get preview; Option<WalletFailure> get previewFailureOption; bool get isPreviewing; ExchangeResult? get result; Option<WalletFailure> get failureOption; bool get isSubmitting; String? get idempotencyKey;
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$ExchangeFormStateCopyWith<ExchangeFormState> get copyWith => _$ExchangeFormStateCopyWithImpl<ExchangeFormState>(this as ExchangeFormState, _$identity);
@override
bool operator ==(Object other) {
final _this = this as ExchangeFormState;
return identical(this, other) || (other.runtimeType == runtimeType&&other is ExchangeFormState&&(identical(other.coins, _this.coins) || other.coins == _this.coins)&&(identical(other.preview, _this.preview) || other.preview == _this.preview)&&(identical(other.previewFailureOption, _this.previewFailureOption) || other.previewFailureOption == _this.previewFailureOption)&&(identical(other.isPreviewing, _this.isPreviewing) || other.isPreviewing == _this.isPreviewing)&&(identical(other.result, _this.result) || other.result == _this.result)&&(identical(other.failureOption, _this.failureOption) || other.failureOption == _this.failureOption)&&(identical(other.isSubmitting, _this.isSubmitting) || other.isSubmitting == _this.isSubmitting)&&(identical(other.idempotencyKey, _this.idempotencyKey) || other.idempotencyKey == _this.idempotencyKey));
}
@override
int get hashCode {
final _this = this as ExchangeFormState;
return Object.hash(runtimeType,_this.coins,_this.preview,_this.previewFailureOption,_this.isPreviewing,_this.result,_this.failureOption,_this.isSubmitting,_this.idempotencyKey);
}
@override
String toString() {
final _this = this as ExchangeFormState;
return 'ExchangeFormState(coins: ${_this.coins}, preview: ${_this.preview}, previewFailureOption: ${_this.previewFailureOption}, isPreviewing: ${_this.isPreviewing}, result: ${_this.result}, failureOption: ${_this.failureOption}, isSubmitting: ${_this.isSubmitting}, idempotencyKey: ${_this.idempotencyKey})';
}
}
/// @nodoc
abstract mixin class $ExchangeFormStateCopyWith<$Res> {
factory $ExchangeFormStateCopyWith(ExchangeFormState value, $Res Function(ExchangeFormState) _then) = _$ExchangeFormStateCopyWithImpl;
@useResult
$Res call({
int coins, ExchangePreview? preview, Option<WalletFailure> previewFailureOption, bool isPreviewing, ExchangeResult? result, Option<WalletFailure> failureOption, bool isSubmitting, String? idempotencyKey
});
$ExchangePreviewCopyWith<$Res>? get preview;$ExchangeResultCopyWith<$Res>? get result;
}
/// @nodoc
class _$ExchangeFormStateCopyWithImpl<$Res>
implements $ExchangeFormStateCopyWith<$Res> {
_$ExchangeFormStateCopyWithImpl(this._self, this._then);
final ExchangeFormState _self;
final $Res Function(ExchangeFormState) _then;
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? coins = null,Object? preview = freezed,Object? previewFailureOption = null,Object? isPreviewing = null,Object? result = freezed,Object? failureOption = null,Object? isSubmitting = null,Object? idempotencyKey = freezed,}) {
return _then(ExchangeFormState(
coins: null == coins ? _self.coins : coins // ignore: cast_nullable_to_non_nullable
as int,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable
as ExchangePreview?,previewFailureOption: null == previewFailureOption ? _self.previewFailureOption : previewFailureOption // ignore: cast_nullable_to_non_nullable
as Option<WalletFailure>,isPreviewing: null == isPreviewing ? _self.isPreviewing : isPreviewing // ignore: cast_nullable_to_non_nullable
as bool,result: freezed == result ? _self.result : result // ignore: cast_nullable_to_non_nullable
as ExchangeResult?,failureOption: null == failureOption ? _self.failureOption : failureOption // ignore: cast_nullable_to_non_nullable
as Option<WalletFailure>,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,idempotencyKey: freezed == idempotencyKey ? _self.idempotencyKey : idempotencyKey // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ExchangePreviewCopyWith<$Res>? get preview {
if (_self.preview == null) {
return null;
}
return $ExchangePreviewCopyWith<$Res>(_self.preview!, (value) {
return _then(_self.copyWith(preview: value));
});
}/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ExchangeResultCopyWith<$Res>? get result {
if (_self.result == null) {
return null;
}
return $ExchangeResultCopyWith<$Res>(_self.result!, (value) {
return _then(_self.copyWith(result: value));
});
}
}
/// Adds pattern-matching-related methods to [ExchangeFormState].
extension ExchangeFormStatePatterns on ExchangeFormState {
/// A variant of `map` that fallback to returning `orElse`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ExchangeFormState value)? $default,{required TResult orElse(),}){
final _that = this;
switch (_that) {
case _ExchangeFormState() when $default != null:
return $default(_that);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// Callbacks receives the raw object, upcasted.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case final Subclass2 value:
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ExchangeFormState value) $default,){
final _that = this;
switch (_that) {
case _ExchangeFormState():
return $default(_that);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `map` that fallback to returning `null`.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case final Subclass value:
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ExchangeFormState value)? $default,){
final _that = this;
switch (_that) {
case _ExchangeFormState() when $default != null:
return $default(_that);case _:
return null;
}
}
/// A variant of `when` that fallback to an `orElse` callback.
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return orElse();
/// }
/// ```
@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int coins, ExchangePreview? preview, Option<WalletFailure> previewFailureOption, bool isPreviewing, ExchangeResult? result, Option<WalletFailure> failureOption, bool isSubmitting, String? idempotencyKey)? $default,{required TResult orElse(),}) {final _that = this;
switch (_that) {
case _ExchangeFormState() when $default != null:
return $default(_that.coins,_that.preview,_that.previewFailureOption,_that.isPreviewing,_that.result,_that.failureOption,_that.isSubmitting,_that.idempotencyKey);case _:
return orElse();
}
}
/// A `switch`-like method, using callbacks.
///
/// As opposed to `map`, this offers destructuring.
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case Subclass2(:final field2):
/// return ...;
/// }
/// ```
@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int coins, ExchangePreview? preview, Option<WalletFailure> previewFailureOption, bool isPreviewing, ExchangeResult? result, Option<WalletFailure> failureOption, bool isSubmitting, String? idempotencyKey) $default,) {final _that = this;
switch (_that) {
case _ExchangeFormState():
return $default(_that.coins,_that.preview,_that.previewFailureOption,_that.isPreviewing,_that.result,_that.failureOption,_that.isSubmitting,_that.idempotencyKey);case _:
throw StateError('Unexpected subclass');
}
}
/// A variant of `when` that fallback to returning `null`
///
/// It is equivalent to doing:
/// ```dart
/// switch (sealedClass) {
/// case Subclass(:final field):
/// return ...;
/// case _:
/// return null;
/// }
/// ```
@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int coins, ExchangePreview? preview, Option<WalletFailure> previewFailureOption, bool isPreviewing, ExchangeResult? result, Option<WalletFailure> failureOption, bool isSubmitting, String? idempotencyKey)? $default,) {final _that = this;
switch (_that) {
case _ExchangeFormState() when $default != null:
return $default(_that.coins,_that.preview,_that.previewFailureOption,_that.isPreviewing,_that.result,_that.failureOption,_that.isSubmitting,_that.idempotencyKey);case _:
return null;
}
}
}
/// @nodoc
class _ExchangeFormState extends ExchangeFormState {
const _ExchangeFormState({required this.coins, this.preview, required this.previewFailureOption, this.isPreviewing = false, this.result, required this.failureOption, this.isSubmitting = false, this.idempotencyKey}): super._();
@override final int coins;
@override final ExchangePreview? preview;
@override final Option<WalletFailure> previewFailureOption;
@override@JsonKey() final bool isPreviewing;
@override final ExchangeResult? result;
@override final Option<WalletFailure> failureOption;
@override@JsonKey() final bool isSubmitting;
@override final String? idempotencyKey;
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override @JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
_$ExchangeFormStateCopyWith<_ExchangeFormState> get copyWith => __$ExchangeFormStateCopyWithImpl<_ExchangeFormState>(this, _$identity);
@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is _ExchangeFormState&&(identical(other.coins, coins) || other.coins == coins)&&(identical(other.preview, preview) || other.preview == preview)&&(identical(other.previewFailureOption, previewFailureOption) || other.previewFailureOption == previewFailureOption)&&(identical(other.isPreviewing, isPreviewing) || other.isPreviewing == isPreviewing)&&(identical(other.result, result) || other.result == result)&&(identical(other.failureOption, failureOption) || other.failureOption == failureOption)&&(identical(other.isSubmitting, isSubmitting) || other.isSubmitting == isSubmitting)&&(identical(other.idempotencyKey, idempotencyKey) || other.idempotencyKey == idempotencyKey));
}
@override
int get hashCode {
return Object.hash(runtimeType,coins,preview,previewFailureOption,isPreviewing,result,failureOption,isSubmitting,idempotencyKey);
}
@override
String toString() {
return 'ExchangeFormState(coins: $coins, preview: $preview, previewFailureOption: $previewFailureOption, isPreviewing: $isPreviewing, result: $result, failureOption: $failureOption, isSubmitting: $isSubmitting, idempotencyKey: $idempotencyKey)';
}
}
/// @nodoc
abstract mixin class _$ExchangeFormStateCopyWith<$Res> implements $ExchangeFormStateCopyWith<$Res> {
factory _$ExchangeFormStateCopyWith(_ExchangeFormState value, $Res Function(_ExchangeFormState) _then) = __$ExchangeFormStateCopyWithImpl;
@override @useResult
$Res call({
int coins, ExchangePreview? preview, Option<WalletFailure> previewFailureOption, bool isPreviewing, ExchangeResult? result, Option<WalletFailure> failureOption, bool isSubmitting, String? idempotencyKey
});
@override $ExchangePreviewCopyWith<$Res>? get preview;@override $ExchangeResultCopyWith<$Res>? get result;
}
/// @nodoc
class __$ExchangeFormStateCopyWithImpl<$Res>
implements _$ExchangeFormStateCopyWith<$Res> {
__$ExchangeFormStateCopyWithImpl(this._self, this._then);
final _ExchangeFormState _self;
final $Res Function(_ExchangeFormState) _then;
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? coins = null,Object? preview = freezed,Object? previewFailureOption = null,Object? isPreviewing = null,Object? result = freezed,Object? failureOption = null,Object? isSubmitting = null,Object? idempotencyKey = freezed,}) {
return _then(_ExchangeFormState(
coins: null == coins ? _self.coins : coins // ignore: cast_nullable_to_non_nullable
as int,preview: freezed == preview ? _self.preview : preview // ignore: cast_nullable_to_non_nullable
as ExchangePreview?,previewFailureOption: null == previewFailureOption ? _self.previewFailureOption : previewFailureOption // ignore: cast_nullable_to_non_nullable
as Option<WalletFailure>,isPreviewing: null == isPreviewing ? _self.isPreviewing : isPreviewing // ignore: cast_nullable_to_non_nullable
as bool,result: freezed == result ? _self.result : result // ignore: cast_nullable_to_non_nullable
as ExchangeResult?,failureOption: null == failureOption ? _self.failureOption : failureOption // ignore: cast_nullable_to_non_nullable
as Option<WalletFailure>,isSubmitting: null == isSubmitting ? _self.isSubmitting : isSubmitting // ignore: cast_nullable_to_non_nullable
as bool,idempotencyKey: freezed == idempotencyKey ? _self.idempotencyKey : idempotencyKey // ignore: cast_nullable_to_non_nullable
as String?,
));
}
/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ExchangePreviewCopyWith<$Res>? get preview {
if (_self.preview == null) {
return null;
}
return $ExchangePreviewCopyWith<$Res>(_self.preview!, (value) {
return _then(_self.copyWith(preview: value));
});
}/// Create a copy of ExchangeFormState
/// with the given fields replaced by the non-null parameter values.
@override
@pragma('vm:prefer-inline')
$ExchangeResultCopyWith<$Res>? get result {
if (_self.result == null) {
return null;
}
return $ExchangeResultCopyWith<$Res>(_self.result!, (value) {
return _then(_self.copyWith(result: value));
});
}
}
// dart format on
@@ -0,0 +1,9 @@
part of 'exchange_form_bloc.dart';
@freezed
abstract class ExchangeFormEvent with _$ExchangeFormEvent {
const factory ExchangeFormEvent.coinsChanged(int coins) = _CoinsChanged;
/// [pin] hanya diteruskan ke API, tidak disimpan di state.
const factory ExchangeFormEvent.submitted(String pin) = _Submitted;
}
@@ -0,0 +1,30 @@
part of 'exchange_form_bloc.dart';
@freezed
abstract class ExchangeFormState with _$ExchangeFormState {
const ExchangeFormState._();
const factory ExchangeFormState({
required int coins,
ExchangePreview? preview,
required Option<WalletFailure> previewFailureOption,
@Default(false) bool isPreviewing,
ExchangeResult? result,
required Option<WalletFailure> failureOption,
@Default(false) bool isSubmitting,
String? idempotencyKey,
}) = _ExchangeFormState;
factory ExchangeFormState.initial() => ExchangeFormState(
coins: 0,
previewFailureOption: none(),
failureOption: none(),
);
bool get canSubmit =>
!isPreviewing &&
!isSubmitting &&
preview != null &&
preview!.valid &&
preview!.coins == coins;
}