✨ Solarpay sheet
This commit is contained in:
parent
3fd9cd4547
commit
4728df93e2
@ -78,7 +78,6 @@
|
||||
"exploreFilterFriends": "Friends",
|
||||
"account": "Account",
|
||||
"name": "Name",
|
||||
"description": "Description",
|
||||
"slug": "Slug",
|
||||
"slugHint": "The slug will be used in the URL to access this resource, it should be unique and URL safe.",
|
||||
"createChatRoom": "Create a Room",
|
||||
@ -467,5 +466,26 @@
|
||||
"preview": "Preview",
|
||||
"togglePreview": "Toggle Preview",
|
||||
"subscribe": "Subscribe",
|
||||
"unsubscribe": "Unsubscribe"
|
||||
"unsubscribe": "Unsubscribe",
|
||||
"paymentVerification": "Payment Verification",
|
||||
"paymentSummary": "Payment Summary",
|
||||
"amount": "Amount",
|
||||
"description": "Description",
|
||||
"pinCode": "PIN Code",
|
||||
"biometric": "Biometric",
|
||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
||||
"clearPin": "Clear PIN",
|
||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||
"authenticating": "Authenticating...",
|
||||
"authenticateNow": "Authenticate Now",
|
||||
"processing": "Processing...",
|
||||
"processingPayment": "Processing Payment...",
|
||||
"pleaseWait": "Please wait",
|
||||
"paymentFailed": "Payment failed. Please try again.",
|
||||
"invalidPin": "Invalid PIN. Please try again.",
|
||||
"biometricAuthFailed": "Biometric authentication failed. Please try again.",
|
||||
"paymentSuccess": "Payment completed successfully!",
|
||||
"membershipPurchaseSuccess": "Membership purchased successfully!",
|
||||
"paymentError": "Payment failed: {error}"
|
||||
}
|
||||
|
@ -313,5 +313,11 @@
|
||||
"checkInResultT1": "凶",
|
||||
"checkInResultT2": "中平",
|
||||
"checkInResultT3": "吉",
|
||||
"checkInResultT4": "大吉"
|
||||
"checkInResultT4": "大吉",
|
||||
"authenticating": "认证中...",
|
||||
"processing": "处理中...",
|
||||
"processingPayment": "处理付款中...",
|
||||
"pleaseWait": "请稍候",
|
||||
"paymentFailed": "付款失败,请重试。",
|
||||
"paymentSuccess": "付款成功完成!"
|
||||
}
|
@ -84,6 +84,8 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_platform_alert (0.0.1):
|
||||
- Flutter
|
||||
- flutter_secure_storage (3.3.1):
|
||||
- Flutter
|
||||
- flutter_timezone (0.0.1):
|
||||
- Flutter
|
||||
- flutter_udid (0.0.1):
|
||||
@ -131,6 +133,9 @@ PODS:
|
||||
- Flutter
|
||||
- flutter_webrtc
|
||||
- WebRTC-SDK (= 125.6422.07)
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- media_kit_libs_ios_video (1.0.4):
|
||||
- Flutter
|
||||
- media_kit_video (0.0.1):
|
||||
@ -210,6 +215,7 @@ DEPENDENCIES:
|
||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_platform_alert (from `.symlinks/plugins/flutter_platform_alert/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- flutter_timezone (from `.symlinks/plugins/flutter_timezone/ios`)
|
||||
- flutter_udid (from `.symlinks/plugins/flutter_udid/ios`)
|
||||
- flutter_webrtc (from `.symlinks/plugins/flutter_webrtc/ios`)
|
||||
@ -218,6 +224,7 @@ DEPENDENCIES:
|
||||
- irondash_engine_context (from `.symlinks/plugins/irondash_engine_context/ios`)
|
||||
- Kingfisher (~> 8.0)
|
||||
- livekit_client (from `.symlinks/plugins/livekit_client/ios`)
|
||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||
- media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`)
|
||||
- media_kit_video (from `.symlinks/plugins/media_kit_video/ios`)
|
||||
- native_exif (from `.symlinks/plugins/native_exif/ios`)
|
||||
@ -277,6 +284,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_native_splash/ios"
|
||||
flutter_platform_alert:
|
||||
:path: ".symlinks/plugins/flutter_platform_alert/ios"
|
||||
flutter_secure_storage:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
flutter_timezone:
|
||||
:path: ".symlinks/plugins/flutter_timezone/ios"
|
||||
flutter_udid:
|
||||
@ -291,6 +300,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/irondash_engine_context/ios"
|
||||
livekit_client:
|
||||
:path: ".symlinks/plugins/livekit_client/ios"
|
||||
local_auth_darwin:
|
||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||
media_kit_libs_ios_video:
|
||||
:path: ".symlinks/plugins/media_kit_libs_ios_video/ios"
|
||||
media_kit_video:
|
||||
@ -341,6 +352,7 @@ SPEC CHECKSUMS:
|
||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||
flutter_platform_alert: bf3b5fcd4ac14bd637e20527e9c471633071afd3
|
||||
flutter_secure_storage: 50035aef357c5a8bdd67fd6bc81370d46efc4d16
|
||||
flutter_timezone: 7c838e17ffd4645d261e87037e5bebf6d38fe544
|
||||
flutter_udid: f7c3884e6ec2951efe4f9de082257fc77c4d15e9
|
||||
flutter_webrtc: fd0d3bdef8766a0736dbbe2e5b7e85f1f3c52117
|
||||
@ -351,6 +363,7 @@ SPEC CHECKSUMS:
|
||||
irondash_engine_context: 8e58ca8e0212ee9d1c7dc6a42121849986c88486
|
||||
Kingfisher: 0621d0ac0c78fecb19f6dc5303bde2b52abaf2f5
|
||||
livekit_client: 9e901890552514206e5ff828903ed271531da264
|
||||
local_auth_darwin: 553ce4f9b16d3fdfeafce9cf042e7c9f77c1c391
|
||||
media_kit_libs_ios_video: 5a18affdb97d1f5d466dc79988b13eff6c5e2854
|
||||
media_kit_video: 1746e198cb697d1ffb734b1d05ec429d1fcd1474
|
||||
nanopb: fad817b59e0457d11a5dfbde799381cd727c1275
|
||||
|
@ -1,5 +1,6 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:island/models/file.dart';
|
||||
import 'package:island/models/wallet.dart';
|
||||
|
||||
part 'user.freezed.dart';
|
||||
part 'user.g.dart';
|
||||
@ -44,6 +45,7 @@ sealed class SnAccountProfile with _$SnAccountProfile {
|
||||
required SnCloudFile? picture,
|
||||
required SnCloudFile? background,
|
||||
required SnVerificationMark? verification,
|
||||
required SnWalletSubscription? stellarMembership,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
|
@ -200,7 +200,7 @@ $SnAccountProfileCopyWith<$Res> get profile {
|
||||
/// @nodoc
|
||||
mixin _$SnAccountProfile {
|
||||
|
||||
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
String get id; String get firstName; String get middleName; String get lastName; String get bio; String get gender; String get pronouns; String get location; String get timeZone; DateTime? get birthday; DateTime? get lastSeenAt; SnAccountBadge? get activeBadge; int get experience; int get level; double get levelingProgress; SnCloudFile? get picture; SnCloudFile? get background; SnVerificationMark? get verification; SnWalletSubscription? get stellarMembership; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@ -213,16 +213,16 @@ $SnAccountProfileCopyWith<SnAccountProfile> get copyWith => _$SnAccountProfileCo
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(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 SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.stellarMembership, stellarMembership) || other.stellarMembership == stellarMembership)&&(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.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
||||
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,stellarMembership,createdAt,updatedAt,deletedAt]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, stellarMembership: $stellarMembership, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
@ -233,11 +233,11 @@ abstract mixin class $SnAccountProfileCopyWith<$Res> {
|
||||
factory $SnAccountProfileCopyWith(SnAccountProfile value, $Res Function(SnAccountProfile) _then) = _$SnAccountProfileCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, SnWalletSubscription? stellarMembership, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
$SnAccountBadgeCopyWith<$Res>? get activeBadge;$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;
|
||||
$SnAccountBadgeCopyWith<$Res>? get activeBadge;$SnCloudFileCopyWith<$Res>? get picture;$SnCloudFileCopyWith<$Res>? get background;$SnVerificationMarkCopyWith<$Res>? get verification;$SnWalletSubscriptionCopyWith<$Res>? get stellarMembership;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@ -250,7 +250,7 @@ class _$SnAccountProfileCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? stellarMembership = freezed,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,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||
@ -270,7 +270,8 @@ as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : lev
|
||||
as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
||||
as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as SnVerificationMark?,stellarMembership: freezed == stellarMembership ? _self.stellarMembership : stellarMembership // ignore: cast_nullable_to_non_nullable
|
||||
as SnWalletSubscription?,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?,
|
||||
@ -324,6 +325,18 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
|
||||
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
||||
return _then(_self.copyWith(verification: value));
|
||||
});
|
||||
}/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnWalletSubscriptionCopyWith<$Res>? get stellarMembership {
|
||||
if (_self.stellarMembership == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnWalletSubscriptionCopyWith<$Res>(_self.stellarMembership!, (value) {
|
||||
return _then(_self.copyWith(stellarMembership: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -332,7 +345,7 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnAccountProfile implements SnAccountProfile {
|
||||
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||
const _SnAccountProfile({required this.id, this.firstName = '', this.middleName = '', this.lastName = '', this.bio = '', this.gender = '', this.pronouns = '', this.location = '', this.timeZone = '', this.birthday, this.lastSeenAt, this.activeBadge, required this.experience, required this.level, required this.levelingProgress, required this.picture, required this.background, required this.verification, required this.stellarMembership, required this.createdAt, required this.updatedAt, required this.deletedAt});
|
||||
factory _SnAccountProfile.fromJson(Map<String, dynamic> json) => _$SnAccountProfileFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@ -353,6 +366,7 @@ class _SnAccountProfile implements SnAccountProfile {
|
||||
@override final SnCloudFile? picture;
|
||||
@override final SnCloudFile? background;
|
||||
@override final SnVerificationMark? verification;
|
||||
@override final SnWalletSubscription? stellarMembership;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
@ -370,16 +384,16 @@ Map<String, dynamic> toJson() {
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(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 _SnAccountProfile&&(identical(other.id, id) || other.id == id)&&(identical(other.firstName, firstName) || other.firstName == firstName)&&(identical(other.middleName, middleName) || other.middleName == middleName)&&(identical(other.lastName, lastName) || other.lastName == lastName)&&(identical(other.bio, bio) || other.bio == bio)&&(identical(other.gender, gender) || other.gender == gender)&&(identical(other.pronouns, pronouns) || other.pronouns == pronouns)&&(identical(other.location, location) || other.location == location)&&(identical(other.timeZone, timeZone) || other.timeZone == timeZone)&&(identical(other.birthday, birthday) || other.birthday == birthday)&&(identical(other.lastSeenAt, lastSeenAt) || other.lastSeenAt == lastSeenAt)&&(identical(other.activeBadge, activeBadge) || other.activeBadge == activeBadge)&&(identical(other.experience, experience) || other.experience == experience)&&(identical(other.level, level) || other.level == level)&&(identical(other.levelingProgress, levelingProgress) || other.levelingProgress == levelingProgress)&&(identical(other.picture, picture) || other.picture == picture)&&(identical(other.background, background) || other.background == background)&&(identical(other.verification, verification) || other.verification == verification)&&(identical(other.stellarMembership, stellarMembership) || other.stellarMembership == stellarMembership)&&(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.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,createdAt,updatedAt,deletedAt]);
|
||||
int get hashCode => Object.hashAll([runtimeType,id,firstName,middleName,lastName,bio,gender,pronouns,location,timeZone,birthday,lastSeenAt,activeBadge,experience,level,levelingProgress,picture,background,verification,stellarMembership,createdAt,updatedAt,deletedAt]);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
return 'SnAccountProfile(id: $id, firstName: $firstName, middleName: $middleName, lastName: $lastName, bio: $bio, gender: $gender, pronouns: $pronouns, location: $location, timeZone: $timeZone, birthday: $birthday, lastSeenAt: $lastSeenAt, activeBadge: $activeBadge, experience: $experience, level: $level, levelingProgress: $levelingProgress, picture: $picture, background: $background, verification: $verification, stellarMembership: $stellarMembership, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
@ -390,11 +404,11 @@ abstract mixin class _$SnAccountProfileCopyWith<$Res> implements $SnAccountProfi
|
||||
factory _$SnAccountProfileCopyWith(_SnAccountProfile value, $Res Function(_SnAccountProfile) _then) = __$SnAccountProfileCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
String id, String firstName, String middleName, String lastName, String bio, String gender, String pronouns, String location, String timeZone, DateTime? birthday, DateTime? lastSeenAt, SnAccountBadge? activeBadge, int experience, int level, double levelingProgress, SnCloudFile? picture, SnCloudFile? background, SnVerificationMark? verification, SnWalletSubscription? stellarMembership, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
@override $SnAccountBadgeCopyWith<$Res>? get activeBadge;@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;
|
||||
@override $SnAccountBadgeCopyWith<$Res>? get activeBadge;@override $SnCloudFileCopyWith<$Res>? get picture;@override $SnCloudFileCopyWith<$Res>? get background;@override $SnVerificationMarkCopyWith<$Res>? get verification;@override $SnWalletSubscriptionCopyWith<$Res>? get stellarMembership;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
@ -407,7 +421,7 @@ class __$SnAccountProfileCopyWithImpl<$Res>
|
||||
|
||||
/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? firstName = null,Object? middleName = null,Object? lastName = null,Object? bio = null,Object? gender = null,Object? pronouns = null,Object? location = null,Object? timeZone = null,Object? birthday = freezed,Object? lastSeenAt = freezed,Object? activeBadge = freezed,Object? experience = null,Object? level = null,Object? levelingProgress = null,Object? picture = freezed,Object? background = freezed,Object? verification = freezed,Object? stellarMembership = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnAccountProfile(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,firstName: null == firstName ? _self.firstName : firstName // ignore: cast_nullable_to_non_nullable
|
||||
@ -427,7 +441,8 @@ as int,levelingProgress: null == levelingProgress ? _self.levelingProgress : lev
|
||||
as double,picture: freezed == picture ? _self.picture : picture // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,background: freezed == background ? _self.background : background // ignore: cast_nullable_to_non_nullable
|
||||
as SnCloudFile?,verification: freezed == verification ? _self.verification : verification // ignore: cast_nullable_to_non_nullable
|
||||
as SnVerificationMark?,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
|
||||
as SnVerificationMark?,stellarMembership: freezed == stellarMembership ? _self.stellarMembership : stellarMembership // ignore: cast_nullable_to_non_nullable
|
||||
as SnWalletSubscription?,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?,
|
||||
@ -482,6 +497,18 @@ $SnVerificationMarkCopyWith<$Res>? get verification {
|
||||
return $SnVerificationMarkCopyWith<$Res>(_self.verification!, (value) {
|
||||
return _then(_self.copyWith(verification: value));
|
||||
});
|
||||
}/// Create a copy of SnAccountProfile
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnWalletSubscriptionCopyWith<$Res>? get stellarMembership {
|
||||
if (_self.stellarMembership == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnWalletSubscriptionCopyWith<$Res>(_self.stellarMembership!, (value) {
|
||||
return _then(_self.copyWith(stellarMembership: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -84,6 +84,12 @@ _SnAccountProfile _$SnAccountProfileFromJson(Map<String, dynamic> json) =>
|
||||
: SnVerificationMark.fromJson(
|
||||
json['verification'] as Map<String, dynamic>,
|
||||
),
|
||||
stellarMembership:
|
||||
json['stellar_membership'] == null
|
||||
? null
|
||||
: SnWalletSubscription.fromJson(
|
||||
json['stellar_membership'] as Map<String, dynamic>,
|
||||
),
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt:
|
||||
@ -112,6 +118,7 @@ Map<String, dynamic> _$SnAccountProfileToJson(_SnAccountProfile instance) =>
|
||||
'picture': instance.picture?.toJson(),
|
||||
'background': instance.background?.toJson(),
|
||||
'verification': instance.verification?.toJson(),
|
||||
'stellar_membership': instance.stellarMembership?.toJson(),
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
|
@ -85,3 +85,29 @@ sealed class SnWalletSubscription with _$SnWalletSubscription {
|
||||
factory SnWalletSubscription.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnWalletSubscriptionFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
sealed class SnWalletOrder with _$SnWalletOrder {
|
||||
const factory SnWalletOrder({
|
||||
required String id,
|
||||
required int status,
|
||||
required String currency,
|
||||
required dynamic remarks,
|
||||
required String appIdentifier,
|
||||
@Default({}) Map<String, dynamic> meta,
|
||||
required int amount,
|
||||
required DateTime expiredAt,
|
||||
required String? payeeWalletId,
|
||||
required SnWallet? payeeWallet,
|
||||
required String? transactionId,
|
||||
required SnTransaction? transaction,
|
||||
required String? issuerAppId,
|
||||
required dynamic issuerApp,
|
||||
required DateTime createdAt,
|
||||
required DateTime updatedAt,
|
||||
required DateTime? deletedAt,
|
||||
}) = _SnWalletOrder;
|
||||
|
||||
factory SnWalletOrder.fromJson(Map<String, dynamic> json) =>
|
||||
_$SnWalletOrderFromJson(json);
|
||||
}
|
||||
|
@ -778,4 +778,239 @@ $SnAccountCopyWith<$Res>? get account {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
mixin _$SnWalletOrder {
|
||||
|
||||
String get id; int get status; String get currency; dynamic get remarks; String get appIdentifier; Map<String, dynamic> get meta; int get amount; DateTime get expiredAt; String? get payeeWalletId; SnWallet? get payeeWallet; String? get transactionId; SnTransaction? get transaction; String? get issuerAppId; dynamic get issuerApp; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnWalletOrderCopyWith<SnWalletOrder> get copyWith => _$SnWalletOrderCopyWithImpl<SnWalletOrder>(this as SnWalletOrder, _$identity);
|
||||
|
||||
/// Serializes this SnWalletOrder to a JSON map.
|
||||
Map<String, dynamic> toJson();
|
||||
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is SnWalletOrder&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.currency, currency) || other.currency == currency)&&const DeepCollectionEquality().equals(other.remarks, remarks)&&(identical(other.appIdentifier, appIdentifier) || other.appIdentifier == appIdentifier)&&const DeepCollectionEquality().equals(other.meta, meta)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.payeeWallet, payeeWallet) || other.payeeWallet == payeeWallet)&&(identical(other.transactionId, transactionId) || other.transactionId == transactionId)&&(identical(other.transaction, transaction) || other.transaction == transaction)&&(identical(other.issuerAppId, issuerAppId) || other.issuerAppId == issuerAppId)&&const DeepCollectionEquality().equals(other.issuerApp, issuerApp)&&(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,status,currency,const DeepCollectionEquality().hash(remarks),appIdentifier,const DeepCollectionEquality().hash(meta),amount,expiredAt,payeeWalletId,payeeWallet,transactionId,transaction,issuerAppId,const DeepCollectionEquality().hash(issuerApp),createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnWalletOrder(id: $id, status: $status, currency: $currency, remarks: $remarks, appIdentifier: $appIdentifier, meta: $meta, amount: $amount, expiredAt: $expiredAt, payeeWalletId: $payeeWalletId, payeeWallet: $payeeWallet, transactionId: $transactionId, transaction: $transaction, issuerAppId: $issuerAppId, issuerApp: $issuerApp, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class $SnWalletOrderCopyWith<$Res> {
|
||||
factory $SnWalletOrderCopyWith(SnWalletOrder value, $Res Function(SnWalletOrder) _then) = _$SnWalletOrderCopyWithImpl;
|
||||
@useResult
|
||||
$Res call({
|
||||
String id, int status, String currency, dynamic remarks, String appIdentifier, Map<String, dynamic> meta, int amount, DateTime expiredAt, String? payeeWalletId, SnWallet? payeeWallet, String? transactionId, SnTransaction? transaction, String? issuerAppId, dynamic issuerApp, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
$SnWalletCopyWith<$Res>? get payeeWallet;$SnTransactionCopyWith<$Res>? get transaction;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class _$SnWalletOrderCopyWithImpl<$Res>
|
||||
implements $SnWalletOrderCopyWith<$Res> {
|
||||
_$SnWalletOrderCopyWithImpl(this._self, this._then);
|
||||
|
||||
final SnWalletOrder _self;
|
||||
final $Res Function(SnWalletOrder) _then;
|
||||
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? status = null,Object? currency = null,Object? remarks = freezed,Object? appIdentifier = null,Object? meta = null,Object? amount = null,Object? expiredAt = null,Object? payeeWalletId = freezed,Object? payeeWallet = freezed,Object? transactionId = freezed,Object? transaction = freezed,Object? issuerAppId = freezed,Object? issuerApp = freezed,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,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,appIdentifier: null == appIdentifier ? _self.appIdentifier : appIdentifier // ignore: cast_nullable_to_non_nullable
|
||||
as String,meta: null == meta ? _self.meta : meta // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
||||
as int,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,payeeWallet: freezed == payeeWallet ? _self.payeeWallet : payeeWallet // ignore: cast_nullable_to_non_nullable
|
||||
as SnWallet?,transactionId: freezed == transactionId ? _self.transactionId : transactionId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,transaction: freezed == transaction ? _self.transaction : transaction // ignore: cast_nullable_to_non_nullable
|
||||
as SnTransaction?,issuerAppId: freezed == issuerAppId ? _self.issuerAppId : issuerAppId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,issuerApp: freezed == issuerApp ? _self.issuerApp : issuerApp // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,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?,
|
||||
));
|
||||
}
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnWalletCopyWith<$Res>? get payeeWallet {
|
||||
if (_self.payeeWallet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnWalletCopyWith<$Res>(_self.payeeWallet!, (value) {
|
||||
return _then(_self.copyWith(payeeWallet: value));
|
||||
});
|
||||
}/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnTransactionCopyWith<$Res>? get transaction {
|
||||
if (_self.transaction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnTransactionCopyWith<$Res>(_self.transaction!, (value) {
|
||||
return _then(_self.copyWith(transaction: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// @nodoc
|
||||
@JsonSerializable()
|
||||
|
||||
class _SnWalletOrder implements SnWalletOrder {
|
||||
const _SnWalletOrder({required this.id, required this.status, required this.currency, required this.remarks, required this.appIdentifier, final Map<String, dynamic> meta = const {}, required this.amount, required this.expiredAt, required this.payeeWalletId, required this.payeeWallet, required this.transactionId, required this.transaction, required this.issuerAppId, required this.issuerApp, required this.createdAt, required this.updatedAt, required this.deletedAt}): _meta = meta;
|
||||
factory _SnWalletOrder.fromJson(Map<String, dynamic> json) => _$SnWalletOrderFromJson(json);
|
||||
|
||||
@override final String id;
|
||||
@override final int status;
|
||||
@override final String currency;
|
||||
@override final dynamic remarks;
|
||||
@override final String appIdentifier;
|
||||
final Map<String, dynamic> _meta;
|
||||
@override@JsonKey() Map<String, dynamic> get meta {
|
||||
if (_meta is EqualUnmodifiableMapView) return _meta;
|
||||
// ignore: implicit_dynamic_type
|
||||
return EqualUnmodifiableMapView(_meta);
|
||||
}
|
||||
|
||||
@override final int amount;
|
||||
@override final DateTime expiredAt;
|
||||
@override final String? payeeWalletId;
|
||||
@override final SnWallet? payeeWallet;
|
||||
@override final String? transactionId;
|
||||
@override final SnTransaction? transaction;
|
||||
@override final String? issuerAppId;
|
||||
@override final dynamic issuerApp;
|
||||
@override final DateTime createdAt;
|
||||
@override final DateTime updatedAt;
|
||||
@override final DateTime? deletedAt;
|
||||
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @JsonKey(includeFromJson: false, includeToJson: false)
|
||||
@pragma('vm:prefer-inline')
|
||||
_$SnWalletOrderCopyWith<_SnWalletOrder> get copyWith => __$SnWalletOrderCopyWithImpl<_SnWalletOrder>(this, _$identity);
|
||||
|
||||
@override
|
||||
Map<String, dynamic> toJson() {
|
||||
return _$SnWalletOrderToJson(this, );
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnWalletOrder&&(identical(other.id, id) || other.id == id)&&(identical(other.status, status) || other.status == status)&&(identical(other.currency, currency) || other.currency == currency)&&const DeepCollectionEquality().equals(other.remarks, remarks)&&(identical(other.appIdentifier, appIdentifier) || other.appIdentifier == appIdentifier)&&const DeepCollectionEquality().equals(other._meta, _meta)&&(identical(other.amount, amount) || other.amount == amount)&&(identical(other.expiredAt, expiredAt) || other.expiredAt == expiredAt)&&(identical(other.payeeWalletId, payeeWalletId) || other.payeeWalletId == payeeWalletId)&&(identical(other.payeeWallet, payeeWallet) || other.payeeWallet == payeeWallet)&&(identical(other.transactionId, transactionId) || other.transactionId == transactionId)&&(identical(other.transaction, transaction) || other.transaction == transaction)&&(identical(other.issuerAppId, issuerAppId) || other.issuerAppId == issuerAppId)&&const DeepCollectionEquality().equals(other.issuerApp, issuerApp)&&(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,status,currency,const DeepCollectionEquality().hash(remarks),appIdentifier,const DeepCollectionEquality().hash(_meta),amount,expiredAt,payeeWalletId,payeeWallet,transactionId,transaction,issuerAppId,const DeepCollectionEquality().hash(issuerApp),createdAt,updatedAt,deletedAt);
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'SnWalletOrder(id: $id, status: $status, currency: $currency, remarks: $remarks, appIdentifier: $appIdentifier, meta: $meta, amount: $amount, expiredAt: $expiredAt, payeeWalletId: $payeeWalletId, payeeWallet: $payeeWallet, transactionId: $transactionId, transaction: $transaction, issuerAppId: $issuerAppId, issuerApp: $issuerApp, createdAt: $createdAt, updatedAt: $updatedAt, deletedAt: $deletedAt)';
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// @nodoc
|
||||
abstract mixin class _$SnWalletOrderCopyWith<$Res> implements $SnWalletOrderCopyWith<$Res> {
|
||||
factory _$SnWalletOrderCopyWith(_SnWalletOrder value, $Res Function(_SnWalletOrder) _then) = __$SnWalletOrderCopyWithImpl;
|
||||
@override @useResult
|
||||
$Res call({
|
||||
String id, int status, String currency, dynamic remarks, String appIdentifier, Map<String, dynamic> meta, int amount, DateTime expiredAt, String? payeeWalletId, SnWallet? payeeWallet, String? transactionId, SnTransaction? transaction, String? issuerAppId, dynamic issuerApp, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
|
||||
});
|
||||
|
||||
|
||||
@override $SnWalletCopyWith<$Res>? get payeeWallet;@override $SnTransactionCopyWith<$Res>? get transaction;
|
||||
|
||||
}
|
||||
/// @nodoc
|
||||
class __$SnWalletOrderCopyWithImpl<$Res>
|
||||
implements _$SnWalletOrderCopyWith<$Res> {
|
||||
__$SnWalletOrderCopyWithImpl(this._self, this._then);
|
||||
|
||||
final _SnWalletOrder _self;
|
||||
final $Res Function(_SnWalletOrder) _then;
|
||||
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? status = null,Object? currency = null,Object? remarks = freezed,Object? appIdentifier = null,Object? meta = null,Object? amount = null,Object? expiredAt = null,Object? payeeWalletId = freezed,Object? payeeWallet = freezed,Object? transactionId = freezed,Object? transaction = freezed,Object? issuerAppId = freezed,Object? issuerApp = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
|
||||
return _then(_SnWalletOrder(
|
||||
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||
as String,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
|
||||
as int,currency: null == currency ? _self.currency : currency // ignore: cast_nullable_to_non_nullable
|
||||
as String,remarks: freezed == remarks ? _self.remarks : remarks // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,appIdentifier: null == appIdentifier ? _self.appIdentifier : appIdentifier // ignore: cast_nullable_to_non_nullable
|
||||
as String,meta: null == meta ? _self._meta : meta // ignore: cast_nullable_to_non_nullable
|
||||
as Map<String, dynamic>,amount: null == amount ? _self.amount : amount // ignore: cast_nullable_to_non_nullable
|
||||
as int,expiredAt: null == expiredAt ? _self.expiredAt : expiredAt // ignore: cast_nullable_to_non_nullable
|
||||
as DateTime,payeeWalletId: freezed == payeeWalletId ? _self.payeeWalletId : payeeWalletId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,payeeWallet: freezed == payeeWallet ? _self.payeeWallet : payeeWallet // ignore: cast_nullable_to_non_nullable
|
||||
as SnWallet?,transactionId: freezed == transactionId ? _self.transactionId : transactionId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,transaction: freezed == transaction ? _self.transaction : transaction // ignore: cast_nullable_to_non_nullable
|
||||
as SnTransaction?,issuerAppId: freezed == issuerAppId ? _self.issuerAppId : issuerAppId // ignore: cast_nullable_to_non_nullable
|
||||
as String?,issuerApp: freezed == issuerApp ? _self.issuerApp : issuerApp // ignore: cast_nullable_to_non_nullable
|
||||
as dynamic,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?,
|
||||
));
|
||||
}
|
||||
|
||||
/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnWalletCopyWith<$Res>? get payeeWallet {
|
||||
if (_self.payeeWallet == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnWalletCopyWith<$Res>(_self.payeeWallet!, (value) {
|
||||
return _then(_self.copyWith(payeeWallet: value));
|
||||
});
|
||||
}/// Create a copy of SnWalletOrder
|
||||
/// with the given fields replaced by the non-null parameter values.
|
||||
@override
|
||||
@pragma('vm:prefer-inline')
|
||||
$SnTransactionCopyWith<$Res>? get transaction {
|
||||
if (_self.transaction == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $SnTransactionCopyWith<$Res>(_self.transaction!, (value) {
|
||||
return _then(_self.copyWith(transaction: value));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// dart format on
|
||||
|
@ -156,3 +156,56 @@ Map<String, dynamic> _$SnWalletSubscriptionToJson(
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
};
|
||||
|
||||
_SnWalletOrder _$SnWalletOrderFromJson(Map<String, dynamic> json) =>
|
||||
_SnWalletOrder(
|
||||
id: json['id'] as String,
|
||||
status: (json['status'] as num).toInt(),
|
||||
currency: json['currency'] as String,
|
||||
remarks: json['remarks'],
|
||||
appIdentifier: json['app_identifier'] as String,
|
||||
meta: json['meta'] as Map<String, dynamic>? ?? const {},
|
||||
amount: (json['amount'] as num).toInt(),
|
||||
expiredAt: DateTime.parse(json['expired_at'] as String),
|
||||
payeeWalletId: json['payee_wallet_id'] as String?,
|
||||
payeeWallet:
|
||||
json['payee_wallet'] == null
|
||||
? null
|
||||
: SnWallet.fromJson(json['payee_wallet'] as Map<String, dynamic>),
|
||||
transactionId: json['transaction_id'] as String?,
|
||||
transaction:
|
||||
json['transaction'] == null
|
||||
? null
|
||||
: SnTransaction.fromJson(
|
||||
json['transaction'] as Map<String, dynamic>,
|
||||
),
|
||||
issuerAppId: json['issuer_app_id'] as String?,
|
||||
issuerApp: json['issuer_app'],
|
||||
createdAt: DateTime.parse(json['created_at'] as String),
|
||||
updatedAt: DateTime.parse(json['updated_at'] as String),
|
||||
deletedAt:
|
||||
json['deleted_at'] == null
|
||||
? null
|
||||
: DateTime.parse(json['deleted_at'] as String),
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$SnWalletOrderToJson(_SnWalletOrder instance) =>
|
||||
<String, dynamic>{
|
||||
'id': instance.id,
|
||||
'status': instance.status,
|
||||
'currency': instance.currency,
|
||||
'remarks': instance.remarks,
|
||||
'app_identifier': instance.appIdentifier,
|
||||
'meta': instance.meta,
|
||||
'amount': instance.amount,
|
||||
'expired_at': instance.expiredAt.toIso8601String(),
|
||||
'payee_wallet_id': instance.payeeWalletId,
|
||||
'payee_wallet': instance.payeeWallet?.toJson(),
|
||||
'transaction_id': instance.transactionId,
|
||||
'transaction': instance.transaction?.toJson(),
|
||||
'issuer_app_id': instance.issuerAppId,
|
||||
'issuer_app': instance.issuerApp,
|
||||
'created_at': instance.createdAt.toIso8601String(),
|
||||
'updated_at': instance.updatedAt.toIso8601String(),
|
||||
'deleted_at': instance.deletedAt?.toIso8601String(),
|
||||
};
|
||||
|
@ -1,13 +1,19 @@
|
||||
import 'package:auto_route/auto_route.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/account/leveling_progress.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/models/user.dart';
|
||||
import 'package:island/models/wallet.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:island/pods/userinfo.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:island/widgets/account/leveling_progress.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:island/widgets/app_scaffold.dart';
|
||||
import 'package:island/widgets/payment/payment_overlay.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
|
||||
@RoutePage()
|
||||
class LevelingScreen extends HookConsumerWidget {
|
||||
@ -57,30 +63,33 @@ class LevelingScreen extends HookConsumerWidget {
|
||||
|
||||
const Gap(24),
|
||||
|
||||
// Placeholder for unlocked content
|
||||
// Membership section
|
||||
_buildMembershipSection(context, ref, user.value!),
|
||||
const Gap(16),
|
||||
|
||||
// Unlocked features section
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHigh,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Unlocked Features',
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
const Gap(16),
|
||||
Container(
|
||||
height: 200,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerLow,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.outline.withOpacity(0.2),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'Unlocked features will be shown here',
|
||||
const Gap(8),
|
||||
Text(
|
||||
'Features unlocked at your current level will be displayed here.',
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -208,6 +217,344 @@ class LevelingScreen extends HookConsumerWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMembershipSection(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
SnAccount user,
|
||||
) {
|
||||
final membership = user.profile.stellarMembership;
|
||||
final isActive = membership?.isActive ?? false;
|
||||
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
colors: [
|
||||
Theme.of(context).colorScheme.primaryContainer,
|
||||
Theme.of(context).colorScheme.secondaryContainer,
|
||||
],
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
isActive ? Icons.star : Icons.star_border,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
size: 24,
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'Stellar Membership',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
|
||||
if (isActive) ...[
|
||||
_buildCurrentMembershipCard(context, membership!),
|
||||
const Gap(16),
|
||||
],
|
||||
|
||||
Text(
|
||||
isActive ? 'Upgrade Your Plan' : 'Choose Your Plan',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
const Gap(12),
|
||||
|
||||
_buildMembershipTiers(context, ref, membership),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCurrentMembershipCard(
|
||||
BuildContext context,
|
||||
SnWalletSubscription membership,
|
||||
) {
|
||||
final tierName = _getMembershipTierName(membership.identifier);
|
||||
final tierColor = _getMembershipTierColor(context, membership.identifier);
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: tierColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: tierColor, width: 1),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.verified, color: tierColor, size: 20),
|
||||
const Gap(8),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Current: $tierName',
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: tierColor,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'Expires: ${_formatDate(membership.endedAt)}',
|
||||
style: Theme.of(context).textTheme.bodySmall?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMembershipTiers(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
SnWalletSubscription? currentMembership,
|
||||
) {
|
||||
final tiers = [
|
||||
{
|
||||
'id': 'solian.stellar.primary',
|
||||
'name': 'Stellar',
|
||||
'price': '10 NS\$ per month',
|
||||
'features': [
|
||||
'Basic features',
|
||||
'Priority support',
|
||||
'Ad-free experience',
|
||||
],
|
||||
'color': Colors.blue,
|
||||
},
|
||||
{
|
||||
'id': 'solian.stellar.nova',
|
||||
'name': 'Nova',
|
||||
'price': '20 NS\$ per month',
|
||||
'features': [
|
||||
'All Primary features',
|
||||
'Advanced customization',
|
||||
'Early access',
|
||||
],
|
||||
'color': Colors.purple,
|
||||
},
|
||||
{
|
||||
'id': 'solian.stellar.supernova',
|
||||
'name': 'Supernova',
|
||||
'price': '30 NS\$ per month',
|
||||
'features': ['All Nova features', 'Exclusive content', 'VIP support'],
|
||||
'color': Colors.orange,
|
||||
},
|
||||
];
|
||||
|
||||
return Column(
|
||||
children:
|
||||
tiers.map((tier) {
|
||||
final isCurrentTier = currentMembership?.identifier == tier['id'];
|
||||
final tierColor = tier['color'] as Color;
|
||||
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 8),
|
||||
child: Material(
|
||||
color: Colors.transparent,
|
||||
child: InkWell(
|
||||
onTap:
|
||||
isCurrentTier
|
||||
? null
|
||||
: () => _purchaseMembership(
|
||||
context,
|
||||
ref,
|
||||
tier['id'] as String,
|
||||
),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isCurrentTier
|
||||
? tierColor.withOpacity(0.1)
|
||||
: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(
|
||||
color:
|
||||
isCurrentTier
|
||||
? tierColor
|
||||
: Theme.of(
|
||||
context,
|
||||
).colorScheme.outline.withOpacity(0.2),
|
||||
width: isCurrentTier ? 2 : 1,
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 4,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: tierColor,
|
||||
borderRadius: BorderRadius.circular(2),
|
||||
),
|
||||
),
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
tier['name'] as String,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: isCurrentTier ? tierColor : null,
|
||||
),
|
||||
),
|
||||
const Gap(8),
|
||||
if (isCurrentTier)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
vertical: 2,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: tierColor,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
'CURRENT',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
tier['price'] as String,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(
|
||||
color:
|
||||
Theme.of(
|
||||
context,
|
||||
).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!isCurrentTier)
|
||||
Icon(
|
||||
Icons.arrow_forward_ios,
|
||||
size: 16,
|
||||
color:
|
||||
Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
String _getMembershipTierName(String identifier) {
|
||||
switch (identifier) {
|
||||
case 'solian.stellar.primary':
|
||||
return 'Primary';
|
||||
case 'solian.stellar.nova':
|
||||
return 'Nova';
|
||||
case 'solian.stellar.supernova':
|
||||
return 'Supernova';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
}
|
||||
|
||||
Color _getMembershipTierColor(BuildContext context, String identifier) {
|
||||
switch (identifier) {
|
||||
case 'solian.stellar.primary':
|
||||
return Colors.blue;
|
||||
case 'solian.stellar.nova':
|
||||
return Colors.purple;
|
||||
case 'solian.stellar.supernova':
|
||||
return Colors.orange;
|
||||
default:
|
||||
return Theme.of(context).colorScheme.primary;
|
||||
}
|
||||
}
|
||||
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.day}/${date.month}/${date.year}';
|
||||
}
|
||||
|
||||
Future<void> _purchaseMembership(
|
||||
BuildContext context,
|
||||
WidgetRef ref,
|
||||
String tierId,
|
||||
) async {
|
||||
final client = ref.watch(apiClientProvider);
|
||||
try {
|
||||
showLoadingModal(context);
|
||||
final resp = await client.post(
|
||||
'/subscriptions',
|
||||
data: {
|
||||
'identifier': tierId,
|
||||
'payment_method': 'solian.wallet',
|
||||
'payment_details': {'currency': 'golds'},
|
||||
'cycle_duration_days': 30,
|
||||
},
|
||||
options: Options(headers: {'X-Noop': true}),
|
||||
);
|
||||
final subscription = SnWalletSubscription.fromJson(resp.data);
|
||||
if (subscription.status == 1) return;
|
||||
final orderResp = await client.post(
|
||||
'/subscriptions/${subscription.identifier}/order',
|
||||
);
|
||||
final order = SnWalletOrder.fromJson(orderResp.data);
|
||||
|
||||
if (context.mounted) hideLoadingModal(context);
|
||||
|
||||
// Show payment overlay to complete the payment
|
||||
if (!context.mounted) return;
|
||||
final paidOrder = await PaymentOverlay.show(
|
||||
context: context,
|
||||
order: order,
|
||||
enableBiometric: true,
|
||||
);
|
||||
|
||||
if (paidOrder != null) {
|
||||
// Payment successful, refresh user info or show success message
|
||||
ref.invalidate(userInfoProvider);
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('membershipPurchaseSuccess'.tr()),
|
||||
backgroundColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (context.mounted) hideLoadingModal(context);
|
||||
showErrorAlert(err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class LevelStairsPainter extends CustomPainter {
|
||||
|
@ -40,7 +40,7 @@ EdgeInsets getTabbedPadding(
|
||||
top: top ?? vertical ?? 0,
|
||||
bottom:
|
||||
effectiveBottom != null
|
||||
? effectiveBottom + MediaQuery.of(context).padding.bottom + 16
|
||||
: MediaQuery.of(context).padding.bottom + 16,
|
||||
? effectiveBottom + MediaQuery.of(context).padding.bottom + 56
|
||||
: MediaQuery.of(context).padding.bottom + 56,
|
||||
);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:island/services/responsive.dart';
|
||||
import 'package:styled_widget/styled_widget.dart';
|
||||
|
||||
export 'content/alert.native.dart'
|
||||
@ -11,9 +12,21 @@ void showSnackBar(
|
||||
String message, {
|
||||
SnackBarAction? action,
|
||||
}) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(message), action: action));
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
action: action,
|
||||
margin:
|
||||
isWideScreen(context)
|
||||
? null
|
||||
: EdgeInsets.fromLTRB(
|
||||
15.0,
|
||||
5.0,
|
||||
15.0,
|
||||
MediaQuery.of(context).padding.bottom + 28,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void clearSnackBar(BuildContext context) {
|
||||
|
243
lib/widgets/payment/README.md
Normal file
243
lib/widgets/payment/README.md
Normal file
@ -0,0 +1,243 @@
|
||||
# Payment Overlay Widget
|
||||
|
||||
A reusable payment verification overlay that supports both 6-digit PIN input and biometric authentication for secure payment processing.
|
||||
|
||||
## Features
|
||||
|
||||
- **6-digit PIN Input**: Secure numeric PIN entry with automatic focus management
|
||||
- **Biometric Authentication**: Support for fingerprint and face recognition
|
||||
- **Order Summary**: Display payment details including amount, description, and remarks
|
||||
- **Integrated API Calls**: Automatically handles payment processing via `/orders/{orderId}/pay`
|
||||
- **Error Handling**: Comprehensive error handling with user-friendly messages
|
||||
- **Loading States**: Visual feedback during payment processing
|
||||
- **Responsive Design**: Adapts to different screen sizes and orientations
|
||||
- **Customizable**: Flexible callbacks and styling options
|
||||
- **Accessibility**: Screen reader support and proper focus management
|
||||
- **Localization**: Full i18n support with easy_localization
|
||||
|
||||
## Usage
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:solian/models/wallet.dart';
|
||||
import 'package:solian/widgets/payment/payment_overlay.dart';
|
||||
|
||||
// Create an order
|
||||
final order = SnWalletOrder(
|
||||
id: 'order_123',
|
||||
amount: 2500, // $25.00 in cents
|
||||
currency: 'USD',
|
||||
description: 'Premium Subscription',
|
||||
remarks: 'Monthly billing',
|
||||
status: 'pending',
|
||||
);
|
||||
|
||||
// Show payment overlay
|
||||
PaymentOverlay.show(
|
||||
context: context,
|
||||
order: order,
|
||||
onPaymentSuccess: (completedOrder) {
|
||||
// Handle successful payment
|
||||
print('Payment completed: ${completedOrder.id}');
|
||||
// Navigate to success page or update UI
|
||||
},
|
||||
onPaymentError: (error) {
|
||||
// Handle payment error
|
||||
print('Payment failed: $error');
|
||||
// Show error message to user
|
||||
},
|
||||
onCancel: () {
|
||||
Navigator.of(context).pop();
|
||||
print('Payment cancelled');
|
||||
},
|
||||
enableBiometric: true,
|
||||
);
|
||||
```
|
||||
|
||||
### Advanced Usage with Loading States
|
||||
|
||||
```dart
|
||||
bool isLoading = false;
|
||||
|
||||
PaymentOverlay.show(
|
||||
context: context,
|
||||
order: order,
|
||||
enableBiometric: true,
|
||||
isLoading: isLoading,
|
||||
onPinSubmit: (String pin) async {
|
||||
setState(() => isLoading = true);
|
||||
try {
|
||||
await processPaymentWithPin(pin);
|
||||
Navigator.of(context).pop();
|
||||
} catch (e) {
|
||||
showErrorDialog(e.toString());
|
||||
} finally {
|
||||
setState(() => isLoading = false);
|
||||
}
|
||||
},
|
||||
onBiometricAuth: () async {
|
||||
setState(() => isLoading = true);
|
||||
try {
|
||||
final authenticated = await authenticateWithBiometrics();
|
||||
if (authenticated) {
|
||||
await processPaymentWithBiometrics();
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorDialog(e.toString());
|
||||
} finally {
|
||||
setState(() => isLoading = false);
|
||||
}
|
||||
},
|
||||
);
|
||||
```
|
||||
|
||||
## Parameters
|
||||
|
||||
### PaymentOverlay.show()
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
|-----------|------|----------|-------------|
|
||||
| `context` | `BuildContext` | ✅ | The build context for showing the overlay |
|
||||
| `order` | `SnWalletOrder` | ✅ | The order to be paid |
|
||||
| `onPaymentSuccess` | `Function(SnWalletOrder)?` | ❌ | Callback when payment succeeds with completed order |
|
||||
| `onPaymentError` | `Function(String)?` | ❌ | Callback when payment fails with error message |
|
||||
| `onCancel` | `VoidCallback?` | ❌ | Callback when payment is cancelled |
|
||||
| `enableBiometric` | `bool` | ❌ | Whether to show biometric option (default: true) |
|
||||
|
||||
## API Integration
|
||||
|
||||
The PaymentOverlay automatically handles payment processing by calling the `/orders/{orderId}/pay` endpoint with the following request body:
|
||||
|
||||
### PIN Payment
|
||||
```json
|
||||
{
|
||||
"pin": "123456"
|
||||
}
|
||||
```
|
||||
|
||||
### Biometric Payment
|
||||
```json
|
||||
{
|
||||
"biometric": true
|
||||
}
|
||||
```
|
||||
|
||||
### Response
|
||||
The API should return the completed `SnWalletOrder` object:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "order_123",
|
||||
"amount": 2500,
|
||||
"currency": "USD",
|
||||
"description": "Premium Subscription",
|
||||
"status": "completed",
|
||||
"processorReference": "txn_abc123",
|
||||
// ... other order fields
|
||||
}
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
The widget handles common HTTP status codes:
|
||||
- `401`: Invalid PIN or biometric authentication failed
|
||||
- `400`: Bad request with custom error message
|
||||
- Other errors: Generic payment failed message
|
||||
|
||||
### Implementation Example
|
||||
|
||||
```dart
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
|
||||
class BiometricService {
|
||||
final LocalAuthentication _auth = LocalAuthentication();
|
||||
|
||||
Future<bool> isAvailable() async {
|
||||
final isAvailable = await _auth.canCheckBiometrics;
|
||||
final isDeviceSupported = await _auth.isDeviceSupported();
|
||||
return isAvailable && isDeviceSupported;
|
||||
}
|
||||
|
||||
Future<bool> authenticate() async {
|
||||
try {
|
||||
final bool didAuthenticate = await _auth.authenticate(
|
||||
localizedReason: 'Please authenticate to complete payment',
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
stickyAuth: true,
|
||||
),
|
||||
);
|
||||
return didAuthenticate;
|
||||
} catch (e) {
|
||||
print('Biometric authentication error: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Localization
|
||||
|
||||
Add these keys to your localization files:
|
||||
|
||||
```json
|
||||
{
|
||||
"paymentVerification": "Payment Verification",
|
||||
"paymentSummary": "Payment Summary",
|
||||
"amount": "Amount",
|
||||
"description": "Description",
|
||||
"pinCode": "PIN Code",
|
||||
"biometric": "Biometric",
|
||||
"enterPinToConfirm": "Enter your 6-digit PIN to confirm payment",
|
||||
"clearPin": "Clear PIN",
|
||||
"useBiometricToConfirm": "Use biometric authentication to confirm payment",
|
||||
"touchSensorToAuthenticate": "Touch the sensor to authenticate",
|
||||
"authenticating": "Authenticating...",
|
||||
"authenticateNow": "Authenticate Now",
|
||||
"confirm": "Confirm",
|
||||
"cancel": "Cancel",
|
||||
"paymentFailed": "Payment failed. Please try again.",
|
||||
"invalidPin": "Invalid PIN. Please try again.",
|
||||
"biometricAuthFailed": "Biometric authentication failed. Please try again.",
|
||||
"paymentSuccess": "Payment completed successfully!",
|
||||
"paymentError": "Payment failed: {error}"
|
||||
}
|
||||
```
|
||||
|
||||
## Styling
|
||||
|
||||
The widget automatically adapts to your app's theme. It uses:
|
||||
|
||||
- `Theme.of(context).colorScheme.primary` for primary elements
|
||||
- `Theme.of(context).colorScheme.surface` for backgrounds
|
||||
- `Theme.of(context).textTheme` for typography
|
||||
|
||||
## Security Considerations
|
||||
|
||||
1. **PIN Handling**: The PIN is passed as a string to your callback. Ensure you handle it securely and don't log it.
|
||||
2. **Biometric Authentication**: Always verify biometric authentication on your backend.
|
||||
3. **Network Security**: Use HTTPS for all payment-related API calls.
|
||||
4. **Data Validation**: Validate all payment data on your backend before processing.
|
||||
|
||||
## Example Integration
|
||||
|
||||
See `payment_overlay_example.dart` for a complete working example that demonstrates:
|
||||
|
||||
- How to show the overlay
|
||||
- Handling PIN and biometric authentication
|
||||
- Processing payments
|
||||
- Error handling
|
||||
- Loading states
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `flutter/material.dart` - Material Design components
|
||||
- `flutter/services.dart` - Input formatters and system services
|
||||
- `flutter_riverpod/flutter_riverpod.dart` - State management and dependency injection
|
||||
- `gap/gap.dart` - Spacing widgets
|
||||
- `material_symbols_icons/symbols.dart` - Material Symbols icons
|
||||
- `easy_localization/easy_localization.dart` - Internationalization
|
||||
- `dio/dio.dart` - HTTP client for API calls
|
||||
- `solian/models/wallet.dart` - Wallet order model
|
||||
- `solian/widgets/common/sheet_scaffold.dart` - Sheet scaffold widget
|
||||
- `solian/pods/network.dart` - API client provider
|
495
lib/widgets/payment/payment_overlay.dart
Normal file
495
lib/widgets/payment/payment_overlay.dart
Normal file
@ -0,0 +1,495 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_otp_text_field/flutter_otp_text_field.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||
import 'package:island/widgets/alert.dart';
|
||||
import 'package:material_symbols_icons/symbols.dart';
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:island/models/wallet.dart';
|
||||
import 'package:island/widgets/content/sheet.dart';
|
||||
import 'package:island/pods/network.dart';
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class PaymentOverlay extends HookConsumerWidget {
|
||||
final SnWalletOrder order;
|
||||
final Function(SnWalletOrder completedOrder)? onPaymentSuccess;
|
||||
final Function(String error)? onPaymentError;
|
||||
final VoidCallback? onCancel;
|
||||
final bool enableBiometric;
|
||||
|
||||
const PaymentOverlay({
|
||||
super.key,
|
||||
required this.order,
|
||||
this.onPaymentSuccess,
|
||||
this.onPaymentError,
|
||||
this.onCancel,
|
||||
this.enableBiometric = true,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
return Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context).viewInsets.bottom,
|
||||
),
|
||||
child: SheetScaffold(
|
||||
titleText: 'Solarpay',
|
||||
heightFactor: 0.7,
|
||||
child: _PaymentContent(
|
||||
order: order,
|
||||
onPaymentSuccess: onPaymentSuccess,
|
||||
onPaymentError: onPaymentError,
|
||||
onCancel: onCancel,
|
||||
enableBiometric: enableBiometric,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<SnWalletOrder?> show({
|
||||
required BuildContext context,
|
||||
required SnWalletOrder order,
|
||||
bool enableBiometric = true,
|
||||
}) {
|
||||
return showModalBottomSheet<SnWalletOrder>(
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
useSafeArea: true,
|
||||
builder:
|
||||
(context) => PaymentOverlay(
|
||||
order: order,
|
||||
enableBiometric: enableBiometric,
|
||||
onPaymentSuccess: (completedOrder) {
|
||||
Navigator.of(context).pop(completedOrder);
|
||||
},
|
||||
onPaymentError: (err) {
|
||||
Navigator.of(context).pop();
|
||||
showErrorAlert(err);
|
||||
},
|
||||
onCancel: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _PaymentContent extends ConsumerStatefulWidget {
|
||||
final SnWalletOrder order;
|
||||
final Function(SnWalletOrder)? onPaymentSuccess;
|
||||
final Function(String)? onPaymentError;
|
||||
final VoidCallback? onCancel;
|
||||
final bool enableBiometric;
|
||||
|
||||
const _PaymentContent({
|
||||
required this.order,
|
||||
this.onPaymentSuccess,
|
||||
this.onPaymentError,
|
||||
this.onCancel,
|
||||
this.enableBiometric = true,
|
||||
});
|
||||
|
||||
@override
|
||||
ConsumerState<_PaymentContent> createState() => _PaymentContentState();
|
||||
}
|
||||
|
||||
class _PaymentContentState extends ConsumerState<_PaymentContent> {
|
||||
static const String _pinStorageKey = 'app_pin_code';
|
||||
static final _secureStorage = FlutterSecureStorage();
|
||||
|
||||
final LocalAuthentication _localAuth = LocalAuthentication();
|
||||
|
||||
String _pin = '';
|
||||
bool _isPinMode = true;
|
||||
bool _hasBiometricSupport = false;
|
||||
bool _hasStoredPin = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeBiometric();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _initializeBiometric() async {
|
||||
try {
|
||||
// Check if biometric is available
|
||||
final isAvailable = await _localAuth.isDeviceSupported();
|
||||
final canCheckBiometrics = await _localAuth.canCheckBiometrics;
|
||||
_hasBiometricSupport = isAvailable && canCheckBiometrics;
|
||||
|
||||
// Check if PIN is stored
|
||||
final storedPin = await _secureStorage.read(key: _pinStorageKey);
|
||||
_hasStoredPin = storedPin != null && storedPin.isNotEmpty;
|
||||
|
||||
// Set initial mode based on stored PIN and biometric support
|
||||
if (_hasStoredPin && _hasBiometricSupport && widget.enableBiometric) {
|
||||
_isPinMode = false;
|
||||
// Automatically trigger biometric authentication
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_authenticateWithBiometric();
|
||||
});
|
||||
} else {
|
||||
_isPinMode = true;
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
} catch (e) {
|
||||
// Fallback to PIN mode if biometric setup fails
|
||||
_isPinMode = true;
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onPinSubmit(String pin) {
|
||||
_pin = pin;
|
||||
if (pin.length == 6) {
|
||||
_processPaymentWithPin(pin);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _processPaymentWithPin(String pin) async {
|
||||
showLoadingModal(context);
|
||||
|
||||
try {
|
||||
// Store PIN securely for future biometric authentication
|
||||
if (_hasBiometricSupport && widget.enableBiometric && !_hasStoredPin) {
|
||||
await _secureStorage.write(key: _pinStorageKey, value: pin);
|
||||
_hasStoredPin = true;
|
||||
}
|
||||
|
||||
await _makePaymentRequest(pin);
|
||||
} catch (err) {
|
||||
widget.onPaymentError?.call(err.toString());
|
||||
_pin = '';
|
||||
} finally {
|
||||
if (mounted) {
|
||||
hideLoadingModal(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _authenticateWithBiometric() async {
|
||||
showLoadingModal(context);
|
||||
|
||||
try {
|
||||
// Perform biometric authentication
|
||||
final bool didAuthenticate = await _localAuth.authenticate(
|
||||
localizedReason: 'biometricPrompt'.tr(),
|
||||
options: const AuthenticationOptions(
|
||||
biometricOnly: true,
|
||||
stickyAuth: true,
|
||||
),
|
||||
);
|
||||
|
||||
if (didAuthenticate) {
|
||||
// Retrieve stored PIN and process payment
|
||||
final storedPin = await _secureStorage.read(key: _pinStorageKey);
|
||||
if (storedPin != null && storedPin.isNotEmpty) {
|
||||
await _makePaymentRequest(storedPin);
|
||||
} else {
|
||||
// Fallback to PIN mode if no stored PIN
|
||||
_fallbackToPinMode('noStoredPin'.tr());
|
||||
}
|
||||
} else {
|
||||
// Biometric authentication failed, fallback to PIN mode
|
||||
_fallbackToPinMode('biometricAuthFailed'.tr());
|
||||
}
|
||||
} catch (err) {
|
||||
// Handle biometric authentication errors
|
||||
String errorMessage = 'biometricAuthFailed'.tr();
|
||||
if (err is PlatformException) {
|
||||
switch (err.code) {
|
||||
case 'NotAvailable':
|
||||
errorMessage = 'biometricNotAvailable'.tr();
|
||||
break;
|
||||
case 'NotEnrolled':
|
||||
errorMessage = 'biometricNotEnrolled'.tr();
|
||||
break;
|
||||
case 'LockedOut':
|
||||
case 'PermanentlyLockedOut':
|
||||
errorMessage = 'biometricLockedOut'.tr();
|
||||
break;
|
||||
default:
|
||||
errorMessage = 'biometricAuthFailed'.tr();
|
||||
}
|
||||
}
|
||||
_fallbackToPinMode(errorMessage);
|
||||
} finally {
|
||||
if (mounted) {
|
||||
hideLoadingModal(context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Unified method for making payment requests with PIN
|
||||
Future<void> _makePaymentRequest(String pin) async {
|
||||
try {
|
||||
final client = ref.read(apiClientProvider);
|
||||
final response = await client.post(
|
||||
'/orders/${widget.order.id}/pay',
|
||||
data: {'pin_code': pin},
|
||||
);
|
||||
|
||||
final completedOrder = SnWalletOrder.fromJson(response.data);
|
||||
widget.onPaymentSuccess?.call(completedOrder);
|
||||
} catch (err) {
|
||||
String errorMessage = 'paymentFailed'.tr();
|
||||
if (err is DioException) {
|
||||
if (err.response?.statusCode == 403 ||
|
||||
err.response?.statusCode == 401) {
|
||||
// PIN is invalid
|
||||
errorMessage = 'invalidPin'.tr();
|
||||
// If this was a biometric attempt with stored PIN, remove the stored PIN
|
||||
if (!_isPinMode) {
|
||||
await _secureStorage.delete(key: _pinStorageKey);
|
||||
_hasStoredPin = false;
|
||||
_fallbackToPinMode(errorMessage);
|
||||
return;
|
||||
}
|
||||
} else if (err.response?.statusCode == 400) {
|
||||
errorMessage = err.response?.data?['error'] ?? errorMessage;
|
||||
}
|
||||
}
|
||||
throw errorMessage;
|
||||
}
|
||||
}
|
||||
|
||||
void _fallbackToPinMode(String? message) {
|
||||
setState(() {
|
||||
_isPinMode = true;
|
||||
});
|
||||
if (message != null && message.isNotEmpty) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(message),
|
||||
backgroundColor: Theme.of(context).colorScheme.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
String _formatCurrency(int amount, String currency) {
|
||||
final value = amount / 100.0;
|
||||
return '${value.toStringAsFixed(2)} $currency';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(20),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Order Summary
|
||||
_buildOrderSummary(),
|
||||
const Gap(32),
|
||||
|
||||
// Authentication Content
|
||||
Expanded(
|
||||
child: _isPinMode ? _buildPinInput() : _buildBiometricAuth(),
|
||||
),
|
||||
|
||||
// Action Buttons
|
||||
const Gap(24),
|
||||
_buildActionButtons(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderSummary() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.surfaceContainerHighest,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Symbols.receipt,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
const Gap(8),
|
||||
Text(
|
||||
'paymentSummary'.tr(),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Gap(12),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'amount'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
),
|
||||
Text(
|
||||
_formatCurrency(widget.order.amount, widget.order.currency),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.order.remarks != null) ...[
|
||||
const Gap(8),
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'description'.tr(),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.bodyMedium?.copyWith(fontWeight: FontWeight.w600),
|
||||
),
|
||||
const Spacer(),
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
widget.order.remarks!,
|
||||
style: Theme.of(context).textTheme.bodyMedium,
|
||||
textAlign: TextAlign.end,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPinInput() {
|
||||
return Column(
|
||||
children: [
|
||||
Text(
|
||||
'enterPinToConfirm'.tr(),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Gap(24),
|
||||
OtpTextField(
|
||||
numberOfFields: 6,
|
||||
borderColor: Theme.of(context).colorScheme.outline,
|
||||
focusedBorderColor: Theme.of(context).colorScheme.primary,
|
||||
showFieldAsBox: true,
|
||||
obscureText: true,
|
||||
keyboardType: TextInputType.number,
|
||||
fieldWidth: 48,
|
||||
fieldHeight: 56,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderWidth: 1,
|
||||
textStyle: Theme.of(
|
||||
context,
|
||||
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.w600),
|
||||
onSubmit: _onPinSubmit,
|
||||
onCodeChanged: (String code) {
|
||||
_pin = code;
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBiometricAuth() {
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 120,
|
||||
height: 120,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.primaryContainer,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
Symbols.fingerprint,
|
||||
size: 64,
|
||||
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||
),
|
||||
),
|
||||
const Gap(24),
|
||||
Text(
|
||||
'useBiometricToConfirm'.tr(),
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Gap(16),
|
||||
Text(
|
||||
'touchSensorToAuthenticate'.tr(),
|
||||
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const Gap(32),
|
||||
ElevatedButton.icon(
|
||||
onPressed: _authenticateWithBiometric,
|
||||
icon: const Icon(Symbols.fingerprint),
|
||||
label: Text('authenticateNow'.tr()),
|
||||
style: ElevatedButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||
),
|
||||
),
|
||||
const Gap(16),
|
||||
TextButton(
|
||||
onPressed: () => _fallbackToPinMode(null),
|
||||
child: Text('usePinInstead'.tr()),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildActionButtons() {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: OutlinedButton(
|
||||
onPressed: widget.onCancel,
|
||||
child: Text('cancel'.tr()),
|
||||
),
|
||||
),
|
||||
if (_isPinMode && _pin.length == 6) ...[
|
||||
const Gap(12),
|
||||
Expanded(
|
||||
child: ElevatedButton(
|
||||
onPressed: () => _processPaymentWithPin(_pin),
|
||||
child: Text('confirm'.tr()),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
@ -9,6 +9,7 @@
|
||||
#include <bitsdojo_window_linux/bitsdojo_window_plugin.h>
|
||||
#include <file_selector_linux/file_selector_plugin.h>
|
||||
#include <flutter_platform_alert/flutter_platform_alert_plugin.h>
|
||||
#include <flutter_secure_storage/flutter_secure_storage_plugin.h>
|
||||
#include <flutter_timezone/flutter_timezone_plugin.h>
|
||||
#include <flutter_udid/flutter_udid_plugin.h>
|
||||
#include <flutter_webrtc/flutter_web_r_t_c_plugin.h>
|
||||
@ -32,6 +33,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
|
||||
g_autoptr(FlPluginRegistrar) flutter_platform_alert_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterPlatformAlertPlugin");
|
||||
flutter_platform_alert_plugin_register_with_registrar(flutter_platform_alert_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_secure_storage_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStoragePlugin");
|
||||
flutter_secure_storage_plugin_register_with_registrar(flutter_secure_storage_registrar);
|
||||
g_autoptr(FlPluginRegistrar) flutter_timezone_registrar =
|
||||
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterTimezonePlugin");
|
||||
flutter_timezone_plugin_register_with_registrar(flutter_timezone_registrar);
|
||||
|
@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
bitsdojo_window_linux
|
||||
file_selector_linux
|
||||
flutter_platform_alert
|
||||
flutter_secure_storage
|
||||
flutter_timezone
|
||||
flutter_udid
|
||||
flutter_webrtc
|
||||
|
@ -20,6 +20,7 @@ import flutter_webrtc
|
||||
import gal
|
||||
import irondash_engine_context
|
||||
import livekit_client
|
||||
import local_auth_darwin
|
||||
import media_kit_libs_macos_video
|
||||
import media_kit_video
|
||||
import package_info_plus
|
||||
@ -51,6 +52,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
||||
GalPlugin.register(with: registry.registrar(forPlugin: "GalPlugin"))
|
||||
IrondashEngineContextPlugin.register(with: registry.registrar(forPlugin: "IrondashEngineContextPlugin"))
|
||||
LiveKitPlugin.register(with: registry.registrar(forPlugin: "LiveKitPlugin"))
|
||||
FLALocalAuthPlugin.register(with: registry.registrar(forPlugin: "FLALocalAuthPlugin"))
|
||||
MediaKitLibsMacosVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitLibsMacosVideoPlugin"))
|
||||
MediaKitVideoPlugin.register(with: registry.registrar(forPlugin: "MediaKitVideoPlugin"))
|
||||
FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin"))
|
||||
|
64
pubspec.lock
64
pubspec.lock
@ -149,10 +149,10 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "7cf79af8eb6023bee797a77b067fb6e63ac5650f3789546e023958098feb776e"
|
||||
sha256: "74273591bd8b7f82eeb1f191c1b65a6576535bbfd5ca3722778b07d5702d33cc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.2"
|
||||
version: "2.5.3"
|
||||
build_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -173,26 +173,26 @@ packages:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_resolvers
|
||||
sha256: "7a507e6026abe52074836d51a945bfad456daa7493eb7a6cac565e490e7d5b54"
|
||||
sha256: badce70566085f2e87434531c4a6bc8e833672f755fc51146d612245947e91c9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.2"
|
||||
version: "2.5.3"
|
||||
build_runner:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: build_runner
|
||||
sha256: "1ce1e5063b564f26c27bda54c82a3d38339df69ec58f90e0017f447de77e4839"
|
||||
sha256: b9070a4127033777c0e63195f6f117ed16a351ed676f6313b095cf4f328c0b82
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.2"
|
||||
version: "2.5.3"
|
||||
build_runner_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build_runner_core
|
||||
sha256: "564230f3fd9363df7870058fef11ec5502ee620aec3b1ee8106b943be5c63a76"
|
||||
sha256: "1cdfece3eeb3f1263f7dbf5bcc0cba697bd0c22d2c866cb4b578c954dbb09bcf"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.1.0"
|
||||
version: "9.1.1"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -891,6 +891,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.1"
|
||||
flutter_secure_storage:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_secure_storage
|
||||
sha256: "9f3dd2ac3b6875b0fde5b04734789c3ef35ba3965c18e99dd564a7a2f8056df6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.1"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1245,6 +1253,46 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.8"
|
||||
local_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: local_auth
|
||||
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
local_auth_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_android
|
||||
sha256: "63ad7ca6396290626dc0cb34725a939e4cfe965d80d36112f08d49cf13a8136e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.49"
|
||||
local_auth_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_darwin
|
||||
sha256: "630996cd7b7f28f5ab92432c4b35d055dd03a747bc319e5ffbb3c4806a3e50d2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.3"
|
||||
local_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_platform_interface
|
||||
sha256: "1b842ff177a7068442eae093b64abe3592f816afd2a533c0ebcdbe40f9d2075a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.10"
|
||||
local_auth_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_windows
|
||||
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.11"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -116,6 +116,8 @@ dependencies:
|
||||
sign_in_with_apple: ^7.0.1
|
||||
flutter_svg: ^2.1.0
|
||||
native_exif: ^0.6.2
|
||||
local_auth: ^2.3.0
|
||||
flutter_secure_storage: ^4.2.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <gal/gal_plugin_c_api.h>
|
||||
#include <irondash_engine_context/irondash_engine_context_plugin_c_api.h>
|
||||
#include <livekit_client/live_kit_plugin.h>
|
||||
#include <local_auth_windows/local_auth_plugin.h>
|
||||
#include <media_kit_libs_windows_video/media_kit_libs_windows_video_plugin_c_api.h>
|
||||
#include <media_kit_video/media_kit_video_plugin_c_api.h>
|
||||
#include <pasteboard/pasteboard_plugin.h>
|
||||
@ -52,6 +53,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||
registry->GetRegistrarForPlugin("IrondashEngineContextPluginCApi"));
|
||||
LiveKitPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LiveKitPlugin"));
|
||||
LocalAuthPluginRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("LocalAuthPlugin"));
|
||||
MediaKitLibsWindowsVideoPluginCApiRegisterWithRegistrar(
|
||||
registry->GetRegistrarForPlugin("MediaKitLibsWindowsVideoPluginCApi"));
|
||||
MediaKitVideoPluginCApiRegisterWithRegistrar(
|
||||
|
@ -15,6 +15,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
|
||||
gal
|
||||
irondash_engine_context
|
||||
livekit_client
|
||||
local_auth_windows
|
||||
media_kit_libs_windows_video
|
||||
media_kit_video
|
||||
pasteboard
|
||||
|
Loading…
x
Reference in New Issue
Block a user