💄 Optimized wallet
This commit is contained in:
@@ -251,6 +251,12 @@
|
|||||||
"translatorBadgeName": "Translator",
|
"translatorBadgeName": "Translator",
|
||||||
"translatorBadgeDescription": "Helping translate Solar Network into different languages",
|
"translatorBadgeDescription": "Helping translate Solar Network into different languages",
|
||||||
"wallet": "Wallet",
|
"wallet": "Wallet",
|
||||||
|
"walletStats": "Wallet Statistics",
|
||||||
|
"totalTransactions": "Total Transactions",
|
||||||
|
"totalOrders": "Total Orders",
|
||||||
|
"totalIncome": "Total Income",
|
||||||
|
"totalOutgoing": "Total Outgoing",
|
||||||
|
"netBalance": "Net Balance",
|
||||||
"walletCurrencyPoints": "New Solar Points",
|
"walletCurrencyPoints": "New Solar Points",
|
||||||
"walletCurrencyShortPoints": "NSP",
|
"walletCurrencyShortPoints": "NSP",
|
||||||
"walletCurrencyGolds": "The Solar Dollars",
|
"walletCurrencyGolds": "The Solar Dollars",
|
||||||
|
@@ -20,6 +20,24 @@ sealed class SnWallet with _$SnWallet {
|
|||||||
_$SnWalletFromJson(json);
|
_$SnWalletFromJson(json);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
sealed class SnWalletStats with _$SnWalletStats {
|
||||||
|
const factory SnWalletStats({
|
||||||
|
required DateTime periodBegin,
|
||||||
|
required DateTime periodEnd,
|
||||||
|
required int totalTransactions,
|
||||||
|
required int totalOrders,
|
||||||
|
required double totalIncome,
|
||||||
|
required double totalOutgoing,
|
||||||
|
required double sum,
|
||||||
|
@Default({}) Map<String, double> incomeCategories,
|
||||||
|
@Default({}) Map<String, double> outgoingCategories,
|
||||||
|
}) = _SnWalletStats;
|
||||||
|
|
||||||
|
factory SnWalletStats.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$SnWalletStatsFromJson(json);
|
||||||
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
sealed class SnWalletPocket with _$SnWalletPocket {
|
sealed class SnWalletPocket with _$SnWalletPocket {
|
||||||
const factory SnWalletPocket({
|
const factory SnWalletPocket({
|
||||||
|
@@ -317,6 +317,299 @@ $SnAccountCopyWith<$Res>? get account {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
mixin _$SnWalletStats {
|
||||||
|
|
||||||
|
DateTime get periodBegin; DateTime get periodEnd; int get totalTransactions; int get totalOrders; double get totalIncome; double get totalOutgoing; double get sum; Map<String, double> get incomeCategories; Map<String, double> get outgoingCategories;
|
||||||
|
/// Create a copy of SnWalletStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
$SnWalletStatsCopyWith<SnWalletStats> get copyWith => _$SnWalletStatsCopyWithImpl<SnWalletStats>(this as SnWalletStats, _$identity);
|
||||||
|
|
||||||
|
/// Serializes this SnWalletStats to a JSON map.
|
||||||
|
Map<String, dynamic> toJson();
|
||||||
|
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnWalletStats&&(identical(other.periodBegin, periodBegin) || other.periodBegin == periodBegin)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.totalTransactions, totalTransactions) || other.totalTransactions == totalTransactions)&&(identical(other.totalOrders, totalOrders) || other.totalOrders == totalOrders)&&(identical(other.totalIncome, totalIncome) || other.totalIncome == totalIncome)&&(identical(other.totalOutgoing, totalOutgoing) || other.totalOutgoing == totalOutgoing)&&(identical(other.sum, sum) || other.sum == sum)&&const DeepCollectionEquality().equals(other.incomeCategories, incomeCategories)&&const DeepCollectionEquality().equals(other.outgoingCategories, outgoingCategories));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,periodBegin,periodEnd,totalTransactions,totalOrders,totalIncome,totalOutgoing,sum,const DeepCollectionEquality().hash(incomeCategories),const DeepCollectionEquality().hash(outgoingCategories));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnWalletStats(periodBegin: $periodBegin, periodEnd: $periodEnd, totalTransactions: $totalTransactions, totalOrders: $totalOrders, totalIncome: $totalIncome, totalOutgoing: $totalOutgoing, sum: $sum, incomeCategories: $incomeCategories, outgoingCategories: $outgoingCategories)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class $SnWalletStatsCopyWith<$Res> {
|
||||||
|
factory $SnWalletStatsCopyWith(SnWalletStats value, $Res Function(SnWalletStats) _then) = _$SnWalletStatsCopyWithImpl;
|
||||||
|
@useResult
|
||||||
|
$Res call({
|
||||||
|
DateTime periodBegin, DateTime periodEnd, int totalTransactions, int totalOrders, double totalIncome, double totalOutgoing, double sum, Map<String, double> incomeCategories, Map<String, double> outgoingCategories
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class _$SnWalletStatsCopyWithImpl<$Res>
|
||||||
|
implements $SnWalletStatsCopyWith<$Res> {
|
||||||
|
_$SnWalletStatsCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final SnWalletStats _self;
|
||||||
|
final $Res Function(SnWalletStats) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnWalletStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@pragma('vm:prefer-inline') @override $Res call({Object? periodBegin = null,Object? periodEnd = null,Object? totalTransactions = null,Object? totalOrders = null,Object? totalIncome = null,Object? totalOutgoing = null,Object? sum = null,Object? incomeCategories = null,Object? outgoingCategories = null,}) {
|
||||||
|
return _then(_self.copyWith(
|
||||||
|
periodBegin: null == periodBegin ? _self.periodBegin : periodBegin // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,periodEnd: null == periodEnd ? _self.periodEnd : periodEnd // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,totalTransactions: null == totalTransactions ? _self.totalTransactions : totalTransactions // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,totalOrders: null == totalOrders ? _self.totalOrders : totalOrders // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,totalIncome: null == totalIncome ? _self.totalIncome : totalIncome // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,totalOutgoing: null == totalOutgoing ? _self.totalOutgoing : totalOutgoing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,sum: null == sum ? _self.sum : sum // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,incomeCategories: null == incomeCategories ? _self.incomeCategories : incomeCategories // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, double>,outgoingCategories: null == outgoingCategories ? _self.outgoingCategories : outgoingCategories // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, double>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Adds pattern-matching-related methods to [SnWalletStats].
|
||||||
|
extension SnWalletStatsPatterns on SnWalletStats {
|
||||||
|
/// 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( _SnWalletStats value)? $default,{required TResult orElse(),}){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats() 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( _SnWalletStats value) $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats():
|
||||||
|
return $default(_that);}
|
||||||
|
}
|
||||||
|
/// 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( _SnWalletStats value)? $default,){
|
||||||
|
final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats() 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( DateTime periodBegin, DateTime periodEnd, int totalTransactions, int totalOrders, double totalIncome, double totalOutgoing, double sum, Map<String, double> incomeCategories, Map<String, double> outgoingCategories)? $default,{required TResult orElse(),}) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats() when $default != null:
|
||||||
|
return $default(_that.periodBegin,_that.periodEnd,_that.totalTransactions,_that.totalOrders,_that.totalIncome,_that.totalOutgoing,_that.sum,_that.incomeCategories,_that.outgoingCategories);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( DateTime periodBegin, DateTime periodEnd, int totalTransactions, int totalOrders, double totalIncome, double totalOutgoing, double sum, Map<String, double> incomeCategories, Map<String, double> outgoingCategories) $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats():
|
||||||
|
return $default(_that.periodBegin,_that.periodEnd,_that.totalTransactions,_that.totalOrders,_that.totalIncome,_that.totalOutgoing,_that.sum,_that.incomeCategories,_that.outgoingCategories);}
|
||||||
|
}
|
||||||
|
/// 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( DateTime periodBegin, DateTime periodEnd, int totalTransactions, int totalOrders, double totalIncome, double totalOutgoing, double sum, Map<String, double> incomeCategories, Map<String, double> outgoingCategories)? $default,) {final _that = this;
|
||||||
|
switch (_that) {
|
||||||
|
case _SnWalletStats() when $default != null:
|
||||||
|
return $default(_that.periodBegin,_that.periodEnd,_that.totalTransactions,_that.totalOrders,_that.totalIncome,_that.totalOutgoing,_that.sum,_that.incomeCategories,_that.outgoingCategories);case _:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
@JsonSerializable()
|
||||||
|
|
||||||
|
class _SnWalletStats implements SnWalletStats {
|
||||||
|
const _SnWalletStats({required this.periodBegin, required this.periodEnd, required this.totalTransactions, required this.totalOrders, required this.totalIncome, required this.totalOutgoing, required this.sum, final Map<String, double> incomeCategories = const {}, final Map<String, double> outgoingCategories = const {}}): _incomeCategories = incomeCategories,_outgoingCategories = outgoingCategories;
|
||||||
|
factory _SnWalletStats.fromJson(Map<String, dynamic> json) => _$SnWalletStatsFromJson(json);
|
||||||
|
|
||||||
|
@override final DateTime periodBegin;
|
||||||
|
@override final DateTime periodEnd;
|
||||||
|
@override final int totalTransactions;
|
||||||
|
@override final int totalOrders;
|
||||||
|
@override final double totalIncome;
|
||||||
|
@override final double totalOutgoing;
|
||||||
|
@override final double sum;
|
||||||
|
final Map<String, double> _incomeCategories;
|
||||||
|
@override@JsonKey() Map<String, double> get incomeCategories {
|
||||||
|
if (_incomeCategories is EqualUnmodifiableMapView) return _incomeCategories;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_incomeCategories);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Map<String, double> _outgoingCategories;
|
||||||
|
@override@JsonKey() Map<String, double> get outgoingCategories {
|
||||||
|
if (_outgoingCategories is EqualUnmodifiableMapView) return _outgoingCategories;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableMapView(_outgoingCategories);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Create a copy of SnWalletStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@pragma('vm:prefer-inline')
|
||||||
|
_$SnWalletStatsCopyWith<_SnWalletStats> get copyWith => __$SnWalletStatsCopyWithImpl<_SnWalletStats>(this, _$identity);
|
||||||
|
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
return _$SnWalletStatsToJson(this, );
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) {
|
||||||
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWalletStats&&(identical(other.periodBegin, periodBegin) || other.periodBegin == periodBegin)&&(identical(other.periodEnd, periodEnd) || other.periodEnd == periodEnd)&&(identical(other.totalTransactions, totalTransactions) || other.totalTransactions == totalTransactions)&&(identical(other.totalOrders, totalOrders) || other.totalOrders == totalOrders)&&(identical(other.totalIncome, totalIncome) || other.totalIncome == totalIncome)&&(identical(other.totalOutgoing, totalOutgoing) || other.totalOutgoing == totalOutgoing)&&(identical(other.sum, sum) || other.sum == sum)&&const DeepCollectionEquality().equals(other._incomeCategories, _incomeCategories)&&const DeepCollectionEquality().equals(other._outgoingCategories, _outgoingCategories));
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
|
@override
|
||||||
|
int get hashCode => Object.hash(runtimeType,periodBegin,periodEnd,totalTransactions,totalOrders,totalIncome,totalOutgoing,sum,const DeepCollectionEquality().hash(_incomeCategories),const DeepCollectionEquality().hash(_outgoingCategories));
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() {
|
||||||
|
return 'SnWalletStats(periodBegin: $periodBegin, periodEnd: $periodEnd, totalTransactions: $totalTransactions, totalOrders: $totalOrders, totalIncome: $totalIncome, totalOutgoing: $totalOutgoing, sum: $sum, incomeCategories: $incomeCategories, outgoingCategories: $outgoingCategories)';
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @nodoc
|
||||||
|
abstract mixin class _$SnWalletStatsCopyWith<$Res> implements $SnWalletStatsCopyWith<$Res> {
|
||||||
|
factory _$SnWalletStatsCopyWith(_SnWalletStats value, $Res Function(_SnWalletStats) _then) = __$SnWalletStatsCopyWithImpl;
|
||||||
|
@override @useResult
|
||||||
|
$Res call({
|
||||||
|
DateTime periodBegin, DateTime periodEnd, int totalTransactions, int totalOrders, double totalIncome, double totalOutgoing, double sum, Map<String, double> incomeCategories, Map<String, double> outgoingCategories
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/// @nodoc
|
||||||
|
class __$SnWalletStatsCopyWithImpl<$Res>
|
||||||
|
implements _$SnWalletStatsCopyWith<$Res> {
|
||||||
|
__$SnWalletStatsCopyWithImpl(this._self, this._then);
|
||||||
|
|
||||||
|
final _SnWalletStats _self;
|
||||||
|
final $Res Function(_SnWalletStats) _then;
|
||||||
|
|
||||||
|
/// Create a copy of SnWalletStats
|
||||||
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
|
@override @pragma('vm:prefer-inline') $Res call({Object? periodBegin = null,Object? periodEnd = null,Object? totalTransactions = null,Object? totalOrders = null,Object? totalIncome = null,Object? totalOutgoing = null,Object? sum = null,Object? incomeCategories = null,Object? outgoingCategories = null,}) {
|
||||||
|
return _then(_SnWalletStats(
|
||||||
|
periodBegin: null == periodBegin ? _self.periodBegin : periodBegin // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,periodEnd: null == periodEnd ? _self.periodEnd : periodEnd // ignore: cast_nullable_to_non_nullable
|
||||||
|
as DateTime,totalTransactions: null == totalTransactions ? _self.totalTransactions : totalTransactions // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,totalOrders: null == totalOrders ? _self.totalOrders : totalOrders // ignore: cast_nullable_to_non_nullable
|
||||||
|
as int,totalIncome: null == totalIncome ? _self.totalIncome : totalIncome // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,totalOutgoing: null == totalOutgoing ? _self.totalOutgoing : totalOutgoing // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,sum: null == sum ? _self.sum : sum // ignore: cast_nullable_to_non_nullable
|
||||||
|
as double,incomeCategories: null == incomeCategories ? _self._incomeCategories : incomeCategories // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, double>,outgoingCategories: null == outgoingCategories ? _self._outgoingCategories : outgoingCategories // ignore: cast_nullable_to_non_nullable
|
||||||
|
as Map<String, double>,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$SnWalletPocket {
|
mixin _$SnWalletPocket {
|
||||||
|
|
||||||
|
@@ -35,6 +35,40 @@ Map<String, dynamic> _$SnWalletToJson(_SnWallet instance) => <String, dynamic>{
|
|||||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_SnWalletStats _$SnWalletStatsFromJson(Map<String, dynamic> json) =>
|
||||||
|
_SnWalletStats(
|
||||||
|
periodBegin: DateTime.parse(json['period_begin'] as String),
|
||||||
|
periodEnd: DateTime.parse(json['period_end'] as String),
|
||||||
|
totalTransactions: (json['total_transactions'] as num).toInt(),
|
||||||
|
totalOrders: (json['total_orders'] as num).toInt(),
|
||||||
|
totalIncome: (json['total_income'] as num).toDouble(),
|
||||||
|
totalOutgoing: (json['total_outgoing'] as num).toDouble(),
|
||||||
|
sum: (json['sum'] as num).toDouble(),
|
||||||
|
incomeCategories:
|
||||||
|
(json['income_categories'] as Map<String, dynamic>?)?.map(
|
||||||
|
(k, e) => MapEntry(k, (e as num).toDouble()),
|
||||||
|
) ??
|
||||||
|
const {},
|
||||||
|
outgoingCategories:
|
||||||
|
(json['outgoing_categories'] as Map<String, dynamic>?)?.map(
|
||||||
|
(k, e) => MapEntry(k, (e as num).toDouble()),
|
||||||
|
) ??
|
||||||
|
const {},
|
||||||
|
);
|
||||||
|
|
||||||
|
Map<String, dynamic> _$SnWalletStatsToJson(_SnWalletStats instance) =>
|
||||||
|
<String, dynamic>{
|
||||||
|
'period_begin': instance.periodBegin.toIso8601String(),
|
||||||
|
'period_end': instance.periodEnd.toIso8601String(),
|
||||||
|
'total_transactions': instance.totalTransactions,
|
||||||
|
'total_orders': instance.totalOrders,
|
||||||
|
'total_income': instance.totalIncome,
|
||||||
|
'total_outgoing': instance.totalOutgoing,
|
||||||
|
'sum': instance.sum,
|
||||||
|
'income_categories': instance.incomeCategories,
|
||||||
|
'outgoing_categories': instance.outgoingCategories,
|
||||||
|
};
|
||||||
|
|
||||||
_SnWalletPocket _$SnWalletPocketFromJson(Map<String, dynamic> json) =>
|
_SnWalletPocket _$SnWalletPocketFromJson(Map<String, dynamic> json) =>
|
||||||
_SnWalletPocket(
|
_SnWalletPocket(
|
||||||
id: json['id'] as String,
|
id: json['id'] as String,
|
||||||
|
@@ -37,6 +37,13 @@ Future<SnWallet?> walletCurrent(Ref ref) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@riverpod
|
||||||
|
Future<SnWalletStats> walletStats(Ref ref) async {
|
||||||
|
final client = ref.watch(apiClientProvider);
|
||||||
|
final resp = await client.get('/id/wallets/stats');
|
||||||
|
return SnWalletStats.fromJson(resp.data);
|
||||||
|
}
|
||||||
|
|
||||||
class CreateFundSheet extends StatefulWidget {
|
class CreateFundSheet extends StatefulWidget {
|
||||||
const CreateFundSheet({super.key});
|
const CreateFundSheet({super.key});
|
||||||
|
|
||||||
@@ -652,21 +659,13 @@ Future<SnWalletFund> walletFund(Ref ref, String fundId) async {
|
|||||||
return SnWalletFund.fromJson(resp.data);
|
return SnWalletFund.fromJson(resp.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@riverpod
|
|
||||||
Future<Map<String, dynamic>> walletFundStats(Ref ref) async {
|
|
||||||
final client = ref.watch(apiClientProvider);
|
|
||||||
final resp = await client.get('/id/wallets/stats');
|
|
||||||
return resp.data as Map<String, dynamic>;
|
|
||||||
}
|
|
||||||
|
|
||||||
class WalletScreen extends HookConsumerWidget {
|
class WalletScreen extends HookConsumerWidget {
|
||||||
const WalletScreen({super.key});
|
const WalletScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final wallet = ref.watch(walletCurrentProvider);
|
final wallet = ref.watch(walletCurrentProvider);
|
||||||
final tabController = useTabController(initialLength: 3);
|
final tabController = useTabController(initialLength: 2);
|
||||||
final fundStats = ref.watch(walletFundStatsProvider);
|
|
||||||
|
|
||||||
Future<void> createWallet() async {
|
Future<void> createWallet() async {
|
||||||
final client = ref.read(apiClientProvider);
|
final client = ref.read(apiClientProvider);
|
||||||
@@ -726,120 +725,73 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
).center();
|
).center();
|
||||||
}
|
}
|
||||||
|
|
||||||
return Column(
|
return NestedScrollView(
|
||||||
children: [
|
headerSliverBuilder:
|
||||||
// Wallet Overview
|
(context, innerBoxIsScrolled) => [
|
||||||
Column(
|
// Wallet Overview
|
||||||
spacing: 8,
|
SliverToBoxAdapter(
|
||||||
children: [
|
child: Column(
|
||||||
// Pockets
|
spacing: 8,
|
||||||
...data.pockets.map(
|
children: [
|
||||||
(pocket) => Card(
|
// Wallet Stats
|
||||||
margin: EdgeInsets.zero,
|
_buildCompactStatsWidget(context, ref),
|
||||||
child: ListTile(
|
// Pockets
|
||||||
leading: Icon(
|
Card(
|
||||||
kCurrencyIconData[pocket.currency] ??
|
margin: EdgeInsets.zero,
|
||||||
Symbols.universal_currency_alt,
|
child: Column(
|
||||||
|
children: [
|
||||||
|
...data.pockets.map(
|
||||||
|
(pocket) => ListTile(
|
||||||
|
leading: Icon(
|
||||||
|
kCurrencyIconData[pocket.currency] ??
|
||||||
|
Symbols.universal_currency_alt,
|
||||||
|
),
|
||||||
|
title:
|
||||||
|
Text(
|
||||||
|
getCurrencyTranslationKey(
|
||||||
|
pocket.currency,
|
||||||
|
),
|
||||||
|
).tr(),
|
||||||
|
subtitle: Text(
|
||||||
|
'${pocket.amount.toStringAsFixed(2)} ${getCurrencyTranslationKey(pocket.currency, isShort: true).tr()}',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
title:
|
],
|
||||||
Text(
|
).padding(horizontal: 12, top: 12),
|
||||||
getCurrencyTranslationKey(pocket.currency),
|
),
|
||||||
).tr(),
|
|
||||||
subtitle: Text(
|
// Tab Bar
|
||||||
'${pocket.amount.toStringAsFixed(2)} ${getCurrencyTranslationKey(pocket.currency, isShort: true).tr()}',
|
SliverToBoxAdapter(
|
||||||
),
|
child: TabBar(
|
||||||
),
|
controller: tabController,
|
||||||
|
tabs: [
|
||||||
|
Tab(text: 'transactions'.tr()),
|
||||||
|
Tab(text: 'myFunds'.tr()),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// Fund Stats
|
|
||||||
fundStats.when(
|
|
||||||
data:
|
|
||||||
(stats) => Card(
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Symbols.celebration,
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.primary,
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
Text(
|
|
||||||
'fundOverview'.tr(),
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
fontWeight: FontWeight.bold,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const Gap(8),
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: _buildStatItem(
|
|
||||||
context,
|
|
||||||
'totalFundsSent'.tr(),
|
|
||||||
'${stats['total_sent'] ?? 0}',
|
|
||||||
Icons.send,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: _buildStatItem(
|
|
||||||
context,
|
|
||||||
'totalFundsReceived'.tr(),
|
|
||||||
'${stats['total_received'] ?? 0}',
|
|
||||||
Icons.call_received,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
loading:
|
|
||||||
() => const Card(
|
|
||||||
margin: EdgeInsets.zero,
|
|
||||||
child: Padding(
|
|
||||||
padding: EdgeInsets.all(16),
|
|
||||||
child: Center(child: CircularProgressIndicator()),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
error: (error, stack) => const SizedBox.shrink(),
|
|
||||||
),
|
|
||||||
],
|
],
|
||||||
).padding(horizontal: 16, vertical: 16),
|
body: TabBarView(
|
||||||
|
controller: tabController,
|
||||||
// Tab Bar
|
children: [
|
||||||
TabBar(
|
// Transactions Tab
|
||||||
controller: tabController,
|
CustomScrollView(
|
||||||
tabs: [
|
slivers: [
|
||||||
Tab(text: 'transactions'.tr()),
|
PagingHelperSliverView(
|
||||||
Tab(text: 'myFunds'.tr()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
|
|
||||||
// Tab Content
|
|
||||||
Expanded(
|
|
||||||
child: TabBarView(
|
|
||||||
controller: tabController,
|
|
||||||
children: [
|
|
||||||
// Transactions Tab
|
|
||||||
PagingHelperView(
|
|
||||||
provider: transactionListNotifierProvider,
|
provider: transactionListNotifierProvider,
|
||||||
futureRefreshable: transactionListNotifierProvider.future,
|
futureRefreshable: transactionListNotifierProvider.future,
|
||||||
notifierRefreshable:
|
notifierRefreshable:
|
||||||
transactionListNotifierProvider.notifier,
|
transactionListNotifierProvider.notifier,
|
||||||
contentBuilder:
|
contentBuilder:
|
||||||
(data, widgetCount, endItemView) => ListView.builder(
|
(
|
||||||
padding: EdgeInsets.zero,
|
data,
|
||||||
|
widgetCount,
|
||||||
|
endItemView,
|
||||||
|
) => SliverList.builder(
|
||||||
itemCount: widgetCount,
|
itemCount: widgetCount,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (index == widgetCount - 1) {
|
if (index == widgetCount - 1) {
|
||||||
@@ -854,10 +806,14 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
key: ValueKey(transaction.id),
|
key: ValueKey(transaction.id),
|
||||||
leading: Icon(
|
leading: Icon(
|
||||||
isIncome
|
isIncome
|
||||||
? Symbols.arrow_upward
|
? Symbols.payment_arrow_down
|
||||||
: Symbols.arrow_downward,
|
: Symbols.paid,
|
||||||
|
),
|
||||||
|
title: Text(
|
||||||
|
transaction.remarks ?? '',
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
),
|
),
|
||||||
title: Text(transaction.remarks ?? ''),
|
|
||||||
subtitle: Text(
|
subtitle: Text(
|
||||||
DateFormat.yMd().add_Hm().format(
|
DateFormat.yMd().add_Hm().format(
|
||||||
transaction.createdAt,
|
transaction.createdAt,
|
||||||
@@ -873,13 +829,13 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
||||||
// My Funds Tab
|
|
||||||
_buildFundsList(context, ref),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
// My Funds Tab
|
||||||
|
_buildFundsList(context, ref),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
error:
|
error:
|
||||||
@@ -1040,6 +996,112 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildCompactStatsWidget(BuildContext context, WidgetRef ref) {
|
||||||
|
final stats = ref.watch(walletStatsProvider);
|
||||||
|
|
||||||
|
return stats.when(
|
||||||
|
data: (statsData) {
|
||||||
|
return Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
'walletStats'.tr(),
|
||||||
|
style: Theme.of(context).textTheme.titleMedium?.copyWith(
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
Text(
|
||||||
|
'${DateFormat.yMd().format(statsData.periodBegin)} - ${DateFormat.yMd().format(statsData.periodEnd)}',
|
||||||
|
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
context,
|
||||||
|
'totalTransactions'.tr(),
|
||||||
|
statsData.totalTransactions.toString(),
|
||||||
|
Symbols.swap_horiz,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
context,
|
||||||
|
'totalOrders'.tr(),
|
||||||
|
statsData.totalOrders.toString(),
|
||||||
|
Symbols.receipt_long,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const Gap(12),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
context,
|
||||||
|
'totalIncome'.tr(),
|
||||||
|
statsData.totalIncome.toStringAsFixed(2),
|
||||||
|
Symbols.arrow_upward,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
context,
|
||||||
|
'totalOutgoing'.tr(),
|
||||||
|
statsData.totalOutgoing.toStringAsFixed(2),
|
||||||
|
Symbols.arrow_downward,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Gap(16),
|
||||||
|
Expanded(
|
||||||
|
child: _buildStatItem(
|
||||||
|
context,
|
||||||
|
'netBalance'.tr(),
|
||||||
|
statsData.sum.toStringAsFixed(2),
|
||||||
|
Symbols.account_balance,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
loading:
|
||||||
|
() => Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.all(16),
|
||||||
|
child: Center(child: CircularProgressIndicator()),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
error:
|
||||||
|
(error, stack) => Card(
|
||||||
|
margin: EdgeInsets.zero,
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Center(child: Text('Error loading stats')),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _handleFundCreation(
|
Future<void> _handleFundCreation(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
@@ -1075,7 +1137,6 @@ class WalletScreen extends HookConsumerWidget {
|
|||||||
// Wait for server to handle order
|
// Wait for server to handle order
|
||||||
await Future.delayed(const Duration(seconds: 1));
|
await Future.delayed(const Duration(seconds: 1));
|
||||||
ref.invalidate(walletFundsProvider);
|
ref.invalidate(walletFundsProvider);
|
||||||
ref.invalidate(walletFundStatsProvider);
|
|
||||||
ref.invalidate(walletCurrentProvider);
|
ref.invalidate(walletCurrentProvider);
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
showSnackBar('fundCreatedSuccessfully'.tr());
|
showSnackBar('fundCreatedSuccessfully'.tr());
|
||||||
|
@@ -24,6 +24,22 @@ final walletCurrentProvider = AutoDisposeFutureProvider<SnWallet?>.internal(
|
|||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
// ignore: unused_element
|
// ignore: unused_element
|
||||||
typedef WalletCurrentRef = AutoDisposeFutureProviderRef<SnWallet?>;
|
typedef WalletCurrentRef = AutoDisposeFutureProviderRef<SnWallet?>;
|
||||||
|
String _$walletStatsHash() => r'23d692a922c2388135be6a46afa73c018762eb57';
|
||||||
|
|
||||||
|
/// See also [walletStats].
|
||||||
|
@ProviderFor(walletStats)
|
||||||
|
final walletStatsProvider = AutoDisposeFutureProvider<SnWalletStats>.internal(
|
||||||
|
walletStats,
|
||||||
|
name: r'walletStatsProvider',
|
||||||
|
debugGetCreateSourceHash:
|
||||||
|
const bool.fromEnvironment('dart.vm.product') ? null : _$walletStatsHash,
|
||||||
|
dependencies: null,
|
||||||
|
allTransitiveDependencies: null,
|
||||||
|
);
|
||||||
|
|
||||||
|
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
||||||
|
// ignore: unused_element
|
||||||
|
typedef WalletStatsRef = AutoDisposeFutureProviderRef<SnWalletStats>;
|
||||||
String _$walletFundsHash() => r'7ceb415f64fcadab2b10461e27b95bf92352c707';
|
String _$walletFundsHash() => r'7ceb415f64fcadab2b10461e27b95bf92352c707';
|
||||||
|
|
||||||
/// Copied from Dart SDK
|
/// Copied from Dart SDK
|
||||||
@@ -443,25 +459,6 @@ class _WalletFundProviderElement
|
|||||||
String get fundId => (origin as WalletFundProvider).fundId;
|
String get fundId => (origin as WalletFundProvider).fundId;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _$walletFundStatsHash() => r'fac8761cf7828fa151e8cc9115416265148bd00e';
|
|
||||||
|
|
||||||
/// See also [walletFundStats].
|
|
||||||
@ProviderFor(walletFundStats)
|
|
||||||
final walletFundStatsProvider =
|
|
||||||
AutoDisposeFutureProvider<Map<String, dynamic>>.internal(
|
|
||||||
walletFundStats,
|
|
||||||
name: r'walletFundStatsProvider',
|
|
||||||
debugGetCreateSourceHash:
|
|
||||||
const bool.fromEnvironment('dart.vm.product')
|
|
||||||
? null
|
|
||||||
: _$walletFundStatsHash,
|
|
||||||
dependencies: null,
|
|
||||||
allTransitiveDependencies: null,
|
|
||||||
);
|
|
||||||
|
|
||||||
@Deprecated('Will be removed in 3.0. Use Ref instead')
|
|
||||||
// ignore: unused_element
|
|
||||||
typedef WalletFundStatsRef = AutoDisposeFutureProviderRef<Map<String, dynamic>>;
|
|
||||||
String _$transactionListNotifierHash() =>
|
String _$transactionListNotifierHash() =>
|
||||||
r'7b777cd44f3351f68f7bd1dd76bfe8b388381bdb';
|
r'7b777cd44f3351f68f7bd1dd76bfe8b388381bdb';
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user