From d4f95bbbf4f855d2eab4057121b209f6480913ac Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Mon, 17 Nov 2025 01:20:49 +0800 Subject: [PATCH] :sparkles: Claim fund --- assets/i18n/en-US.json | 4 +- lib/models/wallet.dart | 3 + lib/models/wallet.freezed.dart | 55 +- lib/models/wallet.g.dart | 6 + lib/pods/chat/messages_notifier.g.dart | 2 +- lib/screens/account.dart | 21 +- lib/screens/wallet.dart | 47 ++ lib/widgets/content/embed/embed_list.dart | 36 +- lib/widgets/wallet/fund_envelope.dart | 596 ++++++++++++++++++++++ lib/widgets/wallet/fund_envelope.g.dart | 151 ++++++ 10 files changed, 876 insertions(+), 45 deletions(-) create mode 100644 lib/widgets/wallet/fund_envelope.dart create mode 100644 lib/widgets/wallet/fund_envelope.g.dart diff --git a/assets/i18n/en-US.json b/assets/i18n/en-US.json index 0039bd9e..41a7796f 100644 --- a/assets/i18n/en-US.json +++ b/assets/i18n/en-US.json @@ -1333,5 +1333,7 @@ "fund": "Fund", "fundsRecent": "Recent Funds", "fundCreateNew": "Create New", - "fundCreateNewHint": "Create a new fund for your message. Select recipients and amount." + "fundCreateNewHint": "Create a new fund for your message. Select recipients and amount.", + "amountOfSplits": "Amount of Splits", + "enterNumberOfSplits": "Enter Splits Amount" } diff --git a/lib/models/wallet.dart b/lib/models/wallet.dart index 818fd10f..4cfc0e70 100644 --- a/lib/models/wallet.dart +++ b/lib/models/wallet.dart @@ -176,6 +176,8 @@ sealed class SnWalletFund with _$SnWalletFund { required String id, required String currency, required double totalAmount, + required double remainingAmount, + required int amountOfSplits, required int splitType, // 0: even, 1: random required int status, // 0: created, 1: partially claimed, 2: fully claimed, 3: expired @@ -184,6 +186,7 @@ sealed class SnWalletFund with _$SnWalletFund { required SnAccount? creatorAccount, required DateTime expiredAt, required List recipients, + required bool isOpen, required DateTime createdAt, required DateTime updatedAt, required DateTime? deletedAt, diff --git a/lib/models/wallet.freezed.dart b/lib/models/wallet.freezed.dart index cf74838d..bd860a6f 100644 --- a/lib/models/wallet.freezed.dart +++ b/lib/models/wallet.freezed.dart @@ -2553,9 +2553,9 @@ $SnWalletSubscriptionCopyWith<$Res>? get subscription { /// @nodoc mixin _$SnWalletFund { - String get id; String get currency; double get totalAmount; int get splitType;// 0: even, 1: random + String get id; String get currency; double get totalAmount; double get remainingAmount; int get amountOfSplits; int get splitType;// 0: even, 1: random int get status;// 0: created, 1: partially claimed, 2: fully claimed, 3: expired - String? get message; String get creatorAccountId; SnAccount? get creatorAccount; DateTime get expiredAt; List get recipients; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; + String? get message; String get creatorAccountId; SnAccount? get creatorAccount; DateTime get expiredAt; List get recipients; bool get isOpen; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt; /// Create a copy of SnWalletFund /// with the given fields replaced by the non-null parameter values. @JsonKey(includeFromJson: false, includeToJson: false) @@ -2568,16 +2568,16 @@ $SnWalletFundCopyWith get copyWith => _$SnWalletFundCopyWithImpl Object.hash(runtimeType,id,currency,totalAmount,splitType,status,message,creatorAccountId,creatorAccount,expiredAt,const DeepCollectionEquality().hash(recipients),createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,currency,totalAmount,remainingAmount,amountOfSplits,splitType,status,message,creatorAccountId,creatorAccount,expiredAt,const DeepCollectionEquality().hash(recipients),isOpen,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnWalletFund(id: $id, currency: $currency, totalAmount: $totalAmount, splitType: $splitType, status: $status, message: $message, creatorAccountId: $creatorAccountId, creatorAccount: $creatorAccount, expiredAt: $expiredAt, recipients: $recipients, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnWalletFund(id: $id, currency: $currency, totalAmount: $totalAmount, remainingAmount: $remainingAmount, amountOfSplits: $amountOfSplits, splitType: $splitType, status: $status, message: $message, creatorAccountId: $creatorAccountId, creatorAccount: $creatorAccount, expiredAt: $expiredAt, recipients: $recipients, isOpen: $isOpen, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -2588,7 +2588,7 @@ abstract mixin class $SnWalletFundCopyWith<$Res> { factory $SnWalletFundCopyWith(SnWalletFund value, $Res Function(SnWalletFund) _then) = _$SnWalletFundCopyWithImpl; @useResult $Res call({ - String id, String currency, double totalAmount, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String currency, double totalAmount, double remainingAmount, int amountOfSplits, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, bool isOpen, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); @@ -2605,19 +2605,22 @@ class _$SnWalletFundCopyWithImpl<$Res> /// Create a copy of SnWalletFund /// with the given fields replaced by the non-null parameter values. -@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? currency = null,Object? totalAmount = null,Object? splitType = null,Object? status = null,Object? message = freezed,Object? creatorAccountId = null,Object? creatorAccount = freezed,Object? expiredAt = null,Object? recipients = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? currency = null,Object? totalAmount = null,Object? remainingAmount = null,Object? amountOfSplits = null,Object? splitType = null,Object? status = null,Object? message = freezed,Object? creatorAccountId = null,Object? creatorAccount = freezed,Object? expiredAt = null,Object? recipients = null,Object? isOpen = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_self.copyWith( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable as String,totalAmount: null == totalAmount ? _self.totalAmount : totalAmount // ignore: cast_nullable_to_non_nullable -as double,splitType: null == splitType ? _self.splitType : splitType // ignore: cast_nullable_to_non_nullable +as double,remainingAmount: null == remainingAmount ? _self.remainingAmount : remainingAmount // ignore: cast_nullable_to_non_nullable +as double,amountOfSplits: null == amountOfSplits ? _self.amountOfSplits : amountOfSplits // ignore: cast_nullable_to_non_nullable +as int,splitType: null == splitType ? _self.splitType : splitType // ignore: cast_nullable_to_non_nullable as int,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as int,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as String?,creatorAccountId: null == creatorAccountId ? _self.creatorAccountId : creatorAccountId // ignore: cast_nullable_to_non_nullable as String,creatorAccount: freezed == creatorAccount ? _self.creatorAccount : creatorAccount // ignore: cast_nullable_to_non_nullable as SnAccount?,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable as DateTime,recipients: null == recipients ? _self.recipients : recipients // ignore: cast_nullable_to_non_nullable -as List,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as List,isOpen: null == isOpen ? _self.isOpen : isOpen // ignore: cast_nullable_to_non_nullable +as bool,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, @@ -2714,10 +2717,10 @@ return $default(_that);case _: /// } /// ``` -@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String currency, double totalAmount, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; +@optionalTypeArgs TResult maybeWhen(TResult Function( String id, String currency, double totalAmount, double remainingAmount, int amountOfSplits, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, bool isOpen, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,{required TResult orElse(),}) {final _that = this; switch (_that) { case _SnWalletFund() when $default != null: -return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: +return $default(_that.id,_that.currency,_that.totalAmount,_that.remainingAmount,_that.amountOfSplits,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.isOpen,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return orElse(); } @@ -2735,10 +2738,10 @@ return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that. /// } /// ``` -@optionalTypeArgs TResult when(TResult Function( String id, String currency, double totalAmount, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; +@optionalTypeArgs TResult when(TResult Function( String id, String currency, double totalAmount, double remainingAmount, int amountOfSplits, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, bool isOpen, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt) $default,) {final _that = this; switch (_that) { case _SnWalletFund(): -return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.createdAt,_that.updatedAt,_that.deletedAt);} +return $default(_that.id,_that.currency,_that.totalAmount,_that.remainingAmount,_that.amountOfSplits,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.isOpen,_that.createdAt,_that.updatedAt,_that.deletedAt);} } /// A variant of `when` that fallback to returning `null` /// @@ -2752,10 +2755,10 @@ return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that. /// } /// ``` -@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String currency, double totalAmount, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; +@optionalTypeArgs TResult? whenOrNull(TResult? Function( String id, String currency, double totalAmount, double remainingAmount, int amountOfSplits, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, bool isOpen, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt)? $default,) {final _that = this; switch (_that) { case _SnWalletFund() when $default != null: -return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: +return $default(_that.id,_that.currency,_that.totalAmount,_that.remainingAmount,_that.amountOfSplits,_that.splitType,_that.status,_that.message,_that.creatorAccountId,_that.creatorAccount,_that.expiredAt,_that.recipients,_that.isOpen,_that.createdAt,_that.updatedAt,_that.deletedAt);case _: return null; } @@ -2767,12 +2770,14 @@ return $default(_that.id,_that.currency,_that.totalAmount,_that.splitType,_that. @JsonSerializable() class _SnWalletFund implements SnWalletFund { - const _SnWalletFund({required this.id, required this.currency, required this.totalAmount, required this.splitType, required this.status, required this.message, required this.creatorAccountId, required this.creatorAccount, required this.expiredAt, required final List recipients, required this.createdAt, required this.updatedAt, required this.deletedAt}): _recipients = recipients; + const _SnWalletFund({required this.id, required this.currency, required this.totalAmount, required this.remainingAmount, required this.amountOfSplits, required this.splitType, required this.status, required this.message, required this.creatorAccountId, required this.creatorAccount, required this.expiredAt, required final List recipients, required this.isOpen, required this.createdAt, required this.updatedAt, required this.deletedAt}): _recipients = recipients; factory _SnWalletFund.fromJson(Map json) => _$SnWalletFundFromJson(json); @override final String id; @override final String currency; @override final double totalAmount; +@override final double remainingAmount; +@override final int amountOfSplits; @override final int splitType; // 0: even, 1: random @override final int status; @@ -2788,6 +2793,7 @@ class _SnWalletFund implements SnWalletFund { return EqualUnmodifiableListView(_recipients); } +@override final bool isOpen; @override final DateTime createdAt; @override final DateTime updatedAt; @override final DateTime? deletedAt; @@ -2805,16 +2811,16 @@ Map toJson() { @override bool operator ==(Object other) { - return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWalletFund&&(identical(other.id, id) || other.id == id)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)&&(identical(other.splitType, splitType) || other.splitType == splitType)&&(identical(other.status, status) || other.status == status)&&(identical(other.message, message) || other.message == message)&&(identical(other.creatorAccountId, creatorAccountId) || other.creatorAccountId == creatorAccountId)&&(identical(other.creatorAccount, creatorAccount) || other.creatorAccount == creatorAccount)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&const DeepCollectionEquality().equals(other._recipients, _recipients)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); + return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWalletFund&&(identical(other.id, id) || other.id == id)&&(identical(other.currency, currency) || other.currency == currency)&&(identical(other.totalAmount, totalAmount) || other.totalAmount == totalAmount)&&(identical(other.remainingAmount, remainingAmount) || other.remainingAmount == remainingAmount)&&(identical(other.amountOfSplits, amountOfSplits) || other.amountOfSplits == amountOfSplits)&&(identical(other.splitType, splitType) || other.splitType == splitType)&&(identical(other.status, status) || other.status == status)&&(identical(other.message, message) || other.message == message)&&(identical(other.creatorAccountId, creatorAccountId) || other.creatorAccountId == creatorAccountId)&&(identical(other.creatorAccount, creatorAccount) || other.creatorAccount == creatorAccount)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&const DeepCollectionEquality().equals(other._recipients, _recipients)&&(identical(other.isOpen, isOpen) || other.isOpen == isOpen)&&(identical(other.createdAt, createdAt) || other.createdAt == createdAt)&&(identical(other.updatedAt, updatedAt) || other.updatedAt == updatedAt)&&(identical(other.deletedAt, deletedAt) || other.deletedAt == deletedAt)); } @JsonKey(includeFromJson: false, includeToJson: false) @override -int get hashCode => Object.hash(runtimeType,id,currency,totalAmount,splitType,status,message,creatorAccountId,creatorAccount,expiredAt,const DeepCollectionEquality().hash(_recipients),createdAt,updatedAt,deletedAt); +int get hashCode => Object.hash(runtimeType,id,currency,totalAmount,remainingAmount,amountOfSplits,splitType,status,message,creatorAccountId,creatorAccount,expiredAt,const DeepCollectionEquality().hash(_recipients),isOpen,createdAt,updatedAt,deletedAt); @override String toString() { - return 'SnWalletFund(id: $id, currency: $currency, totalAmount: $totalAmount, splitType: $splitType, status: $status, message: $message, creatorAccountId: $creatorAccountId, creatorAccount: $creatorAccount, expiredAt: $expiredAt, recipients: $recipients, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; + return 'SnWalletFund(id: $id, currency: $currency, totalAmount: $totalAmount, remainingAmount: $remainingAmount, amountOfSplits: $amountOfSplits, splitType: $splitType, status: $status, message: $message, creatorAccountId: $creatorAccountId, creatorAccount: $creatorAccount, expiredAt: $expiredAt, recipients: $recipients, isOpen: $isOpen, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)'; } @@ -2825,7 +2831,7 @@ abstract mixin class _$SnWalletFundCopyWith<$Res> implements $SnWalletFundCopyWi factory _$SnWalletFundCopyWith(_SnWalletFund value, $Res Function(_SnWalletFund) _then) = __$SnWalletFundCopyWithImpl; @override @useResult $Res call({ - String id, String currency, double totalAmount, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt + String id, String currency, double totalAmount, double remainingAmount, int amountOfSplits, int splitType, int status, String? message, String creatorAccountId, SnAccount? creatorAccount, DateTime expiredAt, List recipients, bool isOpen, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt }); @@ -2842,19 +2848,22 @@ class __$SnWalletFundCopyWithImpl<$Res> /// Create a copy of SnWalletFund /// with the given fields replaced by the non-null parameter values. -@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? currency = null,Object? totalAmount = null,Object? splitType = null,Object? status = null,Object? message = freezed,Object? creatorAccountId = null,Object? creatorAccount = freezed,Object? expiredAt = null,Object? recipients = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { +@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? currency = null,Object? totalAmount = null,Object? remainingAmount = null,Object? amountOfSplits = null,Object? splitType = null,Object? status = null,Object? message = freezed,Object? creatorAccountId = null,Object? creatorAccount = freezed,Object? expiredAt = null,Object? recipients = null,Object? isOpen = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) { return _then(_SnWalletFund( id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable as String,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable as String,totalAmount: null == totalAmount ? _self.totalAmount : totalAmount // ignore: cast_nullable_to_non_nullable -as double,splitType: null == splitType ? _self.splitType : splitType // ignore: cast_nullable_to_non_nullable +as double,remainingAmount: null == remainingAmount ? _self.remainingAmount : remainingAmount // ignore: cast_nullable_to_non_nullable +as double,amountOfSplits: null == amountOfSplits ? _self.amountOfSplits : amountOfSplits // ignore: cast_nullable_to_non_nullable +as int,splitType: null == splitType ? _self.splitType : splitType // ignore: cast_nullable_to_non_nullable as int,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable as int,message: freezed == message ? _self.message : message // ignore: cast_nullable_to_non_nullable as String?,creatorAccountId: null == creatorAccountId ? _self.creatorAccountId : creatorAccountId // ignore: cast_nullable_to_non_nullable as String,creatorAccount: freezed == creatorAccount ? _self.creatorAccount : creatorAccount // ignore: cast_nullable_to_non_nullable as SnAccount?,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable as DateTime,recipients: null == recipients ? _self._recipients : recipients // ignore: cast_nullable_to_non_nullable -as List,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable +as List,isOpen: null == isOpen ? _self.isOpen : isOpen // ignore: cast_nullable_to_non_nullable +as bool,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable as DateTime,updatedAt: null == updatedAt ? _self.updatedAt : updatedAt // ignore: cast_nullable_to_non_nullable as DateTime,deletedAt: freezed == deletedAt ? _self.deletedAt : deletedAt // ignore: cast_nullable_to_non_nullable as DateTime?, diff --git a/lib/models/wallet.g.dart b/lib/models/wallet.g.dart index 61bf94a7..db822b7e 100644 --- a/lib/models/wallet.g.dart +++ b/lib/models/wallet.g.dart @@ -336,6 +336,8 @@ _SnWalletFund _$SnWalletFundFromJson( id: json['id'] as String, currency: json['currency'] as String, totalAmount: (json['total_amount'] as num).toDouble(), + remainingAmount: (json['remaining_amount'] as num).toDouble(), + amountOfSplits: (json['amount_of_splits'] as num).toInt(), splitType: (json['split_type'] as num).toInt(), status: (json['status'] as num).toInt(), message: json['message'] as String?, @@ -349,6 +351,7 @@ _SnWalletFund _$SnWalletFundFromJson( (json['recipients'] as List) .map((e) => SnWalletFundRecipient.fromJson(e as Map)) .toList(), + isOpen: json['is_open'] as bool, createdAt: DateTime.parse(json['created_at'] as String), updatedAt: DateTime.parse(json['updated_at'] as String), deletedAt: @@ -362,6 +365,8 @@ Map _$SnWalletFundToJson(_SnWalletFund instance) => 'id': instance.id, 'currency': instance.currency, 'total_amount': instance.totalAmount, + 'remaining_amount': instance.remainingAmount, + 'amount_of_splits': instance.amountOfSplits, 'split_type': instance.splitType, 'status': instance.status, 'message': instance.message, @@ -369,6 +374,7 @@ Map _$SnWalletFundToJson(_SnWalletFund instance) => 'creator_account': instance.creatorAccount?.toJson(), 'expired_at': instance.expiredAt.toIso8601String(), 'recipients': instance.recipients.map((e) => e.toJson()).toList(), + 'is_open': instance.isOpen, 'created_at': instance.createdAt.toIso8601String(), 'updated_at': instance.updatedAt.toIso8601String(), 'deleted_at': instance.deletedAt?.toIso8601String(), diff --git a/lib/pods/chat/messages_notifier.g.dart b/lib/pods/chat/messages_notifier.g.dart index cf099923..4179d39a 100644 --- a/lib/pods/chat/messages_notifier.g.dart +++ b/lib/pods/chat/messages_notifier.g.dart @@ -6,7 +6,7 @@ part of 'messages_notifier.dart'; // RiverpodGenerator // ************************************************************************** -String _$messagesNotifierHash() => r'c009eb8598e8b5fbcece2d0b5213b2e434edb3b2'; +String _$messagesNotifierHash() => r'fc9c99024a0801efa4894f250aea8bdc6127a0b6'; /// Copied from Dart SDK class _SystemHash { diff --git a/lib/screens/account.dart b/lib/screens/account.dart index fe6eb720..a6062d1e 100644 --- a/lib/screens/account.dart +++ b/lib/screens/account.dart @@ -375,16 +375,17 @@ class AccountScreen extends HookConsumerWidget { ); }, ), - ListTile( - minTileHeight: 48, - leading: const Icon(Symbols.files), - trailing: const Icon(Symbols.chevron_right), - contentPadding: EdgeInsets.symmetric(horizontal: 24), - title: Text('files').tr(), - onTap: () { - context.goNamed('files'); - }, - ), + if (!isWideScreen(context)) + ListTile( + minTileHeight: 48, + leading: const Icon(Symbols.files), + trailing: const Icon(Symbols.chevron_right), + contentPadding: EdgeInsets.symmetric(horizontal: 24), + title: Text('files').tr(), + onTap: () { + context.goNamed('files'); + }, + ), ListTile( minTileHeight: 48, leading: const Icon(Symbols.wallet), diff --git a/lib/screens/wallet.dart b/lib/screens/wallet.dart index 4bc4916a..07b2fce1 100644 --- a/lib/screens/wallet.dart +++ b/lib/screens/wallet.dart @@ -55,6 +55,7 @@ class CreateFundSheet extends StatefulWidget { class _CreateFundSheetState extends State { final amountController = TextEditingController(); + final splitsController = TextEditingController(text: '1'); final messageController = TextEditingController(); String selectedCurrency = 'golds'; int selectedSplitType = 0; // 0: even, 1: random @@ -64,6 +65,7 @@ class _CreateFundSheetState extends State { void dispose() { amountController.dispose(); messageController.dispose(); + splitsController.dispose(); super.dispose(); } @@ -157,6 +159,44 @@ class _CreateFundSheetState extends State { }, ), + const Gap(16), + + // Amount of Splits Section + Text( + 'amountOfSplits'.tr(), + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + color: Theme.of(context).colorScheme.primary, + ), + ), + const Gap(8), + TextField( + controller: splitsController, + keyboardType: TextInputType.number, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + decoration: InputDecoration( + labelText: 'enterNumberOfSplits'.tr(), + hintText: + selectedRecipients.isNotEmpty + ? selectedRecipients.length.toString() + : '1', + border: OutlineInputBorder( + borderRadius: const BorderRadius.all( + Radius.circular(12), + ), + ), + ), + onTapOutside: + (_) => FocusManager.instance.primaryFocus?.unfocus(), + onChanged: (value) { + if (value.isEmpty && selectedRecipients.isNotEmpty) { + splitsController.text = + selectedRecipients.length.toString(); + } + }, + ), + // Split Type Section (only show when there are 2+ recipients) if (selectedRecipients.length >= 2) ...[ const Gap(16), @@ -496,16 +536,23 @@ class _CreateFundSheetState extends State { Future _createFund() async { final amount = double.tryParse(amountController.text); + final splits = int.tryParse(splitsController.text); if (amount == null || amount <= 0) { showErrorAlert('invalidAmount'.tr()); return; } + if (splits == null || splits <= 0) { + showErrorAlert('invalidNumberOfSplits'.tr()); + return; + } + final data = { 'currency': selectedCurrency, 'total_amount': amount, 'split_type': selectedSplitType, + 'amount_of_splits': splits, 'recipient_account_ids': selectedRecipients.map((r) => r.id).toList(), 'message': messageController.text.trim().isEmpty diff --git a/lib/widgets/content/embed/embed_list.dart b/lib/widgets/content/embed/embed_list.dart index a0f22315..f8b77614 100644 --- a/lib/widgets/content/embed/embed_list.dart +++ b/lib/widgets/content/embed/embed_list.dart @@ -3,8 +3,8 @@ import 'package:island/models/embed.dart'; import 'package:island/utils/mapping.dart'; import 'package:island/widgets/content/embed/link.dart'; import 'package:island/widgets/poll/poll_submit.dart'; +import 'package:island/widgets/wallet/fund_envelope.dart'; import 'package:material_symbols_icons/material_symbols_icons.dart'; -import 'package:styled_widget/styled_widget.dart'; class EmbedListWidget extends StatelessWidget { final List embeds; @@ -96,16 +96,32 @@ class EmbedListWidget extends StatelessWidget { horizontal: renderingPadding.horizontal, vertical: 8, ), - child: - embedData['id'] == null - ? const Text('Poll was unavailable...') - : PollSubmit( - pollId: embedData['id'], - onSubmit: (_) {}, - isReadonly: !isInteractive, - isInitiallyExpanded: isFullPost, - ).padding(horizontal: 16, vertical: 12), + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16, + vertical: 12, + ), + child: + embedData['id'] == null + ? const Text('Poll was unavailable...') + : PollSubmit( + pollId: embedData['id'], + onSubmit: (_) {}, + isReadonly: !isInteractive, + isInitiallyExpanded: isFullPost, + ), + ), ), + 'fund' => + embedData['id'] == null + ? const Text('Fund envelope was unavailable...') + : FundEnvelopeWidget( + fundId: embedData['id'], + margin: EdgeInsets.symmetric( + horizontal: renderingPadding.horizontal, + vertical: 8, + ), + ), _ => Text('Unable show embed: ${embedData['type']}'), }, ), diff --git a/lib/widgets/wallet/fund_envelope.dart b/lib/widgets/wallet/fund_envelope.dart new file mode 100644 index 00000000..1f0507c3 --- /dev/null +++ b/lib/widgets/wallet/fund_envelope.dart @@ -0,0 +1,596 @@ +import 'package:flutter/material.dart'; +import 'package:hooks_riverpod/hooks_riverpod.dart'; +import 'package:island/models/wallet.dart'; +import 'package:island/pods/network.dart'; +import 'package:island/pods/userinfo.dart'; +import 'package:riverpod_annotation/riverpod_annotation.dart'; +import 'package:easy_localization/easy_localization.dart'; + +part 'fund_envelope.g.dart'; + +@riverpod +Future walletFund(Ref ref, String fundId) async { + final apiClient = ref.watch(apiClientProvider); + final resp = await apiClient.get('/pass/wallets/funds/$fundId'); + return SnWalletFund.fromJson(resp.data); +} + +class FundEnvelopeWidget extends HookConsumerWidget { + const FundEnvelopeWidget({ + super.key, + required this.fundId, + this.maxWidth, + this.margin, + }); + + final String fundId; + final double? maxWidth; + final EdgeInsetsGeometry? margin; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final fundAsync = ref.watch(walletFundProvider(fundId)); + + return Container( + width: maxWidth, + margin: margin ?? const EdgeInsets.symmetric(vertical: 8), + child: fundAsync.when( + 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: Column( + children: [ + Icon( + Icons.error_outline, + color: Theme.of(context).colorScheme.error, + ), + const SizedBox(height: 8), + Text( + 'Failed to load fund envelope', + style: TextStyle( + color: Theme.of(context).colorScheme.error, + ), + ), + ], + ), + ), + ), + data: + (fund) => Card( + margin: EdgeInsets.zero, + clipBehavior: Clip.antiAlias, + child: InkWell( + onTap: () => _showClaimDialog(context, ref, fund), + borderRadius: BorderRadius.circular(8), + child: Padding( + padding: const EdgeInsets.all(16), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Fund title and status + Row( + children: [ + Icon( + Icons.account_balance_wallet, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(width: 8), + Expanded( + child: Text( + 'Fund Envelope', + style: Theme.of(context).textTheme.titleMedium + ?.copyWith(fontWeight: FontWeight.w600), + ), + ), + _buildStatusChips(context, fund), + ], + ), + const SizedBox(height: 12), + + // Amount information + Row( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + '${fund.totalAmount.toStringAsFixed(2)} ${fund.currency}', + style: Theme.of( + context, + ).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + const SizedBox(height: 4), + if (fund.remainingAmount != fund.totalAmount) + Text( + 'Remaining: ${fund.remainingAmount.toStringAsFixed(2)} ${fund.currency}', + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith( + color: + Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.w500, + ), + ), + Text( + 'Split: ${fund.splitType == 0 ? 'Evenly' : 'Randomly'}', + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith( + color: Theme.of(context) + .textTheme + .bodySmall + ?.color + ?.withOpacity(0.7), + ), + ), + ], + ), + ], + ), + + // Recipients overview + if (fund.recipients.isNotEmpty) ...[ + const SizedBox(height: 12), + _buildRecipientsOverview(context, fund), + ], + + // Message + if (fund.message != null && fund.message!.isNotEmpty) ...[ + const SizedBox(height: 12), + Text( + '"${fund.message}"', + style: Theme.of( + context, + ).textTheme.bodyMedium?.copyWith( + fontStyle: FontStyle.italic, + color: + Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ], + + // Creator info + if (fund.creatorAccount != null) ...[ + const SizedBox(height: 12), + Row( + children: [ + Icon( + Icons.person, + size: 16, + color: + Theme.of( + context, + ).colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 4), + Text( + fund.creatorAccount!.nick, + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith( + color: + Theme.of( + context, + ).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ], + + // Expiry info + const SizedBox(height: 6), + Row( + children: [ + Icon( + Icons.schedule, + size: 16, + color: + Theme.of(context).colorScheme.onSurfaceVariant, + ), + const SizedBox(width: 4), + Text( + _formatDate(fund.expiredAt), + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith( + color: + Theme.of( + context, + ).colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ], + ), + ), + ), + ), + ), + ); + } + + void _showClaimDialog( + BuildContext context, + WidgetRef ref, + SnWalletFund fund, + ) { + showDialog( + context: context, + builder: + (dialogContext) => FundClaimDialog( + fund: fund, + onClaim: () async { + try { + final apiClient = ref.read(apiClientProvider); + await apiClient.post('/pass/wallets/funds/${fund.id}/receive'); + + // Refresh the fund data after claiming + ref.invalidate(walletFundProvider(fund.id)); + + if (dialogContext.mounted) { + Navigator.of(dialogContext).pop(); + ScaffoldMessenger.of(dialogContext).showSnackBar( + SnackBar(content: Text('Fund claimed successfully!'.tr())), + ); + } + } catch (e) { + if (dialogContext.mounted) { + ScaffoldMessenger.of(dialogContext).showSnackBar( + SnackBar( + content: Text('Failed to claim fund: $e'), + backgroundColor: + Theme.of(dialogContext).colorScheme.error, + ), + ); + } + } + }, + ), + ); + } + + Widget _buildStatusChip(BuildContext context, int status) { + String text; + Color color; + + switch (status) { + case 0: + text = 'Created'; + color = Colors.blue; + break; + case 1: + text = 'Partially Claimed'; + color = Colors.orange; + break; + case 2: + text = 'Fully Claimed'; + color = Colors.green; + break; + case 3: + text = 'Expired'; + color = Colors.red; + break; + default: + text = 'Unknown'; + color = Colors.grey; + } + + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: color.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: color.withOpacity(0.3)), + ), + child: Text( + text, + style: TextStyle( + fontSize: 10, + color: color, + fontWeight: FontWeight.w500, + ), + ), + ); + } + + Widget _buildStatusChips(BuildContext context, SnWalletFund fund) { + return Row( + children: [ + if (fund.isOpen) ...[ + _buildOpenFundBadge(context), + const SizedBox(width: 6), + ], + _buildStatusChip(context, fund.status), + ], + ); + } + + Widget _buildOpenFundBadge(BuildContext context) { + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 2), + decoration: BoxDecoration( + color: Colors.green.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all(color: Colors.green.withOpacity(0.3)), + ), + child: Text( + 'Open Fund'.tr(), + style: const TextStyle( + fontSize: 10, + color: Colors.green, + fontWeight: FontWeight.w500, + ), + ), + ); + } + + Widget _buildRecipientsOverview(BuildContext context, SnWalletFund fund) { + final claimedCount = fund.recipients.where((r) => r.isReceived).length; + final totalCount = + fund.isOpen ? fund.amountOfSplits : fund.recipients.length; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Recipients ($claimedCount/$totalCount claimed)', + style: TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: 4), + LinearProgressIndicator( + value: totalCount > 0 ? claimedCount / totalCount : 0, + backgroundColor: + Theme.of(context).colorScheme.surfaceContainerHighest, + ), + ], + ); + } + + String _formatDate(DateTime date) { + try { + final now = DateTime.now(); + final difference = date.difference(now); + + if (difference.isNegative) { + return 'Expired ${difference.inDays.abs()} days ago'; + } else if (difference.inDays == 0) { + final hours = difference.inHours; + if (hours == 0) { + return 'Expires soon'; + } + return 'Expires in $hours hour${hours == 1 ? '' : 's'}'; + } else if (difference.inDays < 7) { + return 'Expires in ${difference.inDays} day${difference.inDays == 1 ? '' : 's'}'; + } else { + return '${date.day}/${date.month}/${date.year}'; + } + } catch (e) { + return date.toString(); + } + } +} + +class FundClaimDialog extends HookConsumerWidget { + const FundClaimDialog({super.key, required this.fund, required this.onClaim}); + + final SnWalletFund fund; + final VoidCallback onClaim; + + @override + Widget build(BuildContext context, WidgetRef ref) { + final userInfo = ref.watch(userInfoProvider); + + // Check if user can claim + final now = DateTime.now(); + final isExpired = fund.expiredAt.isBefore(now); + final hasRemainingAmount = fund.remainingAmount > 0; + final hasUserClaimed = + userInfo.value != null && + fund.recipients.any( + (recipient) => + recipient.recipientAccountId == userInfo.value!.id && + recipient.isReceived, + ); + final userAbleToClaim = + userInfo.value != null && + (fund.isOpen || + fund.recipients.any( + (recipient) => recipient.recipientAccountId == userInfo.value!.id, + )); + + final canClaim = + !isExpired && hasRemainingAmount && !hasUserClaimed && userAbleToClaim; + + // Get claimed recipients for display + final claimedRecipients = + fund.recipients.where((r) => r.isReceived).toList(); + final unclaimedRecipients = + fund.recipients.where((r) => !r.isReceived).toList(); + + final remainingSplits = + fund.isOpen + ? fund.amountOfSplits - claimedRecipients.length + : unclaimedRecipients.length; + + return AlertDialog( + title: Row( + children: [ + Icon( + Icons.account_balance_wallet, + color: Theme.of(context).colorScheme.primary, + ), + const SizedBox(width: 8), + Text('Claim Fund'.tr()), + ], + ), + content: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + // Fund info + Text( + '${fund.totalAmount.toStringAsFixed(2)} ${fund.currency}', + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.primary, + ), + ), + + // Remaining amount + Text( + '${fund.remainingAmount.toStringAsFixed(2)} ${fund.currency} / ${remainingSplits} splits', + style: Theme.of(context).textTheme.bodyMedium?.copyWith( + color: Theme.of(context).colorScheme.secondary, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 8), + + // Status indicator + Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: + fund.isOpen + ? Colors.green.withOpacity(0.1) + : Colors.blue.withOpacity(0.1), + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: + fund.isOpen + ? Colors.green.withOpacity(0.3) + : Colors.blue.withOpacity(0.3), + ), + ), + child: Text( + fund.isOpen ? 'Open Fund'.tr() : 'Invite Only'.tr(), + style: TextStyle( + fontSize: 12, + color: fund.isOpen ? Colors.green : Colors.blue, + fontWeight: FontWeight.w500, + ), + ), + ), + + const SizedBox(height: 16), + + // Claimed recipients section + if (claimedRecipients.isNotEmpty) ...[ + Text( + 'Already Claimed'.tr(), + style: Theme.of( + context, + ).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 8), + ...claimedRecipients.map( + (recipient) => Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + children: [ + Icon(Icons.check_circle, size: 16, color: Colors.green), + const SizedBox(width: 8), + Expanded( + child: Text( + recipient.recipientAccount?.nick ?? 'Unknown User', + style: Theme.of( + context, + ).textTheme.bodySmall?.copyWith( + decoration: TextDecoration.lineThrough, + color: + Theme.of(context).colorScheme.onSurfaceVariant, + ), + ), + ), + Text( + '${recipient.amount.toStringAsFixed(2)} ${fund.currency}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + ), + ), + const SizedBox(height: 12), + ], + + // Unclaimed recipients section + if (unclaimedRecipients.isNotEmpty) ...[ + Text( + 'Available to Claim'.tr(), + style: Theme.of( + context, + ).textTheme.titleSmall?.copyWith(fontWeight: FontWeight.w600), + ), + const SizedBox(height: 8), + ...unclaimedRecipients.map( + (recipient) => Padding( + padding: const EdgeInsets.only(bottom: 4), + child: Row( + children: [ + Icon( + Icons.radio_button_unchecked, + size: 16, + color: Theme.of( + context, + ).colorScheme.onSurfaceVariant.withOpacity(0.5), + ), + const SizedBox(width: 8), + Expanded( + child: Text( + recipient.recipientAccount?.nick ?? 'Unknown User', + style: Theme.of(context).textTheme.bodySmall, + ), + ), + Text( + '${recipient.amount.toStringAsFixed(2)} ${fund.currency}', + style: Theme.of(context).textTheme.bodySmall?.copyWith( + fontWeight: FontWeight.w500, + color: Theme.of(context).colorScheme.primary, + ), + ), + ], + ), + ), + ), + ], + ], + ), + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: Text('Cancel'.tr()), + ), + if (canClaim) + FilledButton.icon( + icon: const Icon(Icons.account_balance_wallet), + label: Text('Claim'.tr()), + onPressed: onClaim, + ), + ], + ); + } +} diff --git a/lib/widgets/wallet/fund_envelope.g.dart b/lib/widgets/wallet/fund_envelope.g.dart new file mode 100644 index 00000000..5afa8987 --- /dev/null +++ b/lib/widgets/wallet/fund_envelope.g.dart @@ -0,0 +1,151 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'fund_envelope.dart'; + +// ************************************************************************** +// RiverpodGenerator +// ************************************************************************** + +String _$walletFundHash() => r'521fa280708e71266f8164268ba11f135f4ba810'; + +/// Copied from Dart SDK +class _SystemHash { + _SystemHash._(); + + static int combine(int hash, int value) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + value); + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10)); + return hash ^ (hash >> 6); + } + + static int finish(int hash) { + // ignore: parameter_assignments + hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3)); + // ignore: parameter_assignments + hash = hash ^ (hash >> 11); + return 0x1fffffff & (hash + ((0x00003fff & hash) << 15)); + } +} + +/// See also [walletFund]. +@ProviderFor(walletFund) +const walletFundProvider = WalletFundFamily(); + +/// See also [walletFund]. +class WalletFundFamily extends Family> { + /// See also [walletFund]. + const WalletFundFamily(); + + /// See also [walletFund]. + WalletFundProvider call(String fundId) { + return WalletFundProvider(fundId); + } + + @override + WalletFundProvider getProviderOverride( + covariant WalletFundProvider provider, + ) { + return call(provider.fundId); + } + + static const Iterable? _dependencies = null; + + @override + Iterable? get dependencies => _dependencies; + + static const Iterable? _allTransitiveDependencies = null; + + @override + Iterable? get allTransitiveDependencies => + _allTransitiveDependencies; + + @override + String? get name => r'walletFundProvider'; +} + +/// See also [walletFund]. +class WalletFundProvider extends AutoDisposeFutureProvider { + /// See also [walletFund]. + WalletFundProvider(String fundId) + : this._internal( + (ref) => walletFund(ref as WalletFundRef, fundId), + from: walletFundProvider, + name: r'walletFundProvider', + debugGetCreateSourceHash: + const bool.fromEnvironment('dart.vm.product') + ? null + : _$walletFundHash, + dependencies: WalletFundFamily._dependencies, + allTransitiveDependencies: WalletFundFamily._allTransitiveDependencies, + fundId: fundId, + ); + + WalletFundProvider._internal( + super._createNotifier, { + required super.name, + required super.dependencies, + required super.allTransitiveDependencies, + required super.debugGetCreateSourceHash, + required super.from, + required this.fundId, + }) : super.internal(); + + final String fundId; + + @override + Override overrideWith( + FutureOr Function(WalletFundRef provider) create, + ) { + return ProviderOverride( + origin: this, + override: WalletFundProvider._internal( + (ref) => create(ref as WalletFundRef), + from: from, + name: null, + dependencies: null, + allTransitiveDependencies: null, + debugGetCreateSourceHash: null, + fundId: fundId, + ), + ); + } + + @override + AutoDisposeFutureProviderElement createElement() { + return _WalletFundProviderElement(this); + } + + @override + bool operator ==(Object other) { + return other is WalletFundProvider && other.fundId == fundId; + } + + @override + int get hashCode { + var hash = _SystemHash.combine(0, runtimeType.hashCode); + hash = _SystemHash.combine(hash, fundId.hashCode); + + return _SystemHash.finish(hash); + } +} + +@Deprecated('Will be removed in 3.0. Use Ref instead') +// ignore: unused_element +mixin WalletFundRef on AutoDisposeFutureProviderRef { + /// The parameter `fundId` of this provider. + String get fundId; +} + +class _WalletFundProviderElement + extends AutoDisposeFutureProviderElement + with WalletFundRef { + _WalletFundProviderElement(super.provider); + + @override + String get fundId => (origin as WalletFundProvider).fundId; +} + +// ignore_for_file: type=lint +// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package