Finishing up stellar program

This commit is contained in:
LittleSheep 2025-06-22 19:56:54 +08:00
parent 4728df93e2
commit 35b79d7562
8 changed files with 178 additions and 131 deletions

View File

@ -255,7 +255,7 @@
"walletCurrencyPoints": "New Solar Points",
"walletCurrencyShortPoints": "NSP",
"walletCurrencyGolds": "The Solar Dollars",
"walletCurrencyShortGolds": "TSD",
"walletCurrencyShortGolds": "NSD",
"retry": "Retry",
"creatorHubUnselectedHint": "Pick / create a publisher to get started.",
"relationships": "Relationships",
@ -487,5 +487,31 @@
"biometricAuthFailed": "Biometric authentication failed. Please try again.",
"paymentSuccess": "Payment completed successfully!",
"membershipPurchaseSuccess": "Membership purchased successfully!",
"paymentError": "Payment failed: {error}"
"paymentError": "Payment failed: {error}",
"usePinInstead": "Use PIN Code",
"levelProgress": "Level Progress",
"unlockedFeatures": "Unlocked Features",
"unlockedFeaturesDescription": "Features unlocked at your current level will be displayed here.",
"stellarMembership": "Stellar Membership",
"upgradeYourPlan": "Upgrade Your Plan",
"chooseYourPlan": "Choose Your Plan",
"currentMembership": "Current: {}",
"membershipExpires": "Expires: {}",
"membershipTierStellar": "Stellar",
"membershipTierNova": "Nova",
"membershipTierSupernova": "Supernova",
"membershipTierUnknown": "Unknown",
"membershipPriceStellar": "10 NS$ per month",
"membershipPriceNova": "20 NS$ per month",
"membershipPriceSupernova": "30 NS$ per month",
"membershipFeatureBasic": "Basic features",
"membershipFeaturePrioritySupport": "Priority support",
"membershipFeatureAdFree": "Ad-free experience",
"membershipFeatureAllPrimary": "All Primary features",
"membershipFeatureAdvancedCustomization": "Advanced customization",
"membershipFeatureEarlyAccess": "Early access",
"membershipFeatureAllNova": "All Nova features",
"membershipFeatureExclusiveContent": "Exclusive content",
"membershipFeatureVipSupport": "VIP support",
"membershipCurrentBadge": "CURRENT"
}

View File

@ -307,5 +307,32 @@
"chatBreakCleared": "聊天暫停已清除。",
"chatBreakCustom": "自訂時長",
"chatBreakEnterMinutes": "輸入分鐘數",
"chatBreakNone": "無"
"chatBreakNone": "無",
"paymentError": "付款失敗:{error}",
"usePinInstead": "使用密碼",
"levelProgress": "等級進度",
"unlockedFeatures": "已解鎖功能",
"unlockedFeaturesDescription": "您目前等級解鎖的功能將會顯示在此。",
"stellarMembership": "星際會員",
"upgradeYourPlan": "升級您的方案",
"chooseYourPlan": "選擇您的方案",
"currentMembership": "目前:{}",
"membershipExpires": "到期:{}",
"membershipTierStellar": "星際",
"membershipTierNova": "新星",
"membershipTierSupernova": "超新星",
"membershipTierUnknown": "未知",
"membershipPriceStellar": "每月 10 星幣",
"membershipPriceNova": "每月 20 星幣",
"membershipPriceSupernova": "每月 30 星幣",
"membershipFeatureBasic": "基本功能",
"membershipFeaturePrioritySupport": "優先支援",
"membershipFeatureAdFree": "無廣告體驗",
"membershipFeatureAllPrimary": "所有主要功能",
"membershipFeatureAdvancedCustomization": "進階自訂",
"membershipFeatureEarlyAccess": "搶先體驗",
"membershipFeatureAllNova": "所有新星功能",
"membershipFeatureExclusiveContent": "獨家內容",
"membershipFeatureVipSupport": "VIP 支援",
"membershipCurrentBadge": "目前"
}

View File

@ -74,6 +74,8 @@
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>NSFaceIDUsageDescription</key>
<string>Allow the Solar Network verify your ownership of the logged in account and continue your action quickly.</string>
<key>NSUserActivityTypes</key>
<array>
<string>INStartCallIntent</string>

View File

@ -62,21 +62,21 @@ sealed class SnWalletSubscription with _$SnWalletSubscription {
const factory SnWalletSubscription({
required String id,
required DateTime begunAt,
required DateTime endedAt,
required DateTime? endedAt,
required String identifier,
required bool isActive,
required bool isFreeTrial,
required int status,
required String paymentMethod,
required Map<String, dynamic> paymentDetails,
required double basePrice,
@Default(true) bool isActive,
@Default(false) bool isFreeTrial,
@Default(1) int status,
required String? paymentMethod,
required Map<String, dynamic>? paymentDetails,
required double? basePrice,
required String? couponId,
required dynamic coupon,
required DateTime renewalAt,
required DateTime? renewalAt,
required String accountId,
required SnAccount? account,
required bool isAvailable,
required double finalPrice,
@Default(true) bool isAvailable,
required double? finalPrice,
required DateTime createdAt,
required DateTime updatedAt,
required DateTime? deletedAt,

View File

@ -562,7 +562,7 @@ $SnWalletCopyWith<$Res>? get payeeWallet {
/// @nodoc
mixin _$SnWalletSubscription {
String get id; DateTime get begunAt; DateTime get endedAt; String get identifier; bool get isActive; bool get isFreeTrial; int get status; String get paymentMethod; Map<String, dynamic> get paymentDetails; double get basePrice; String? get couponId; dynamic get coupon; DateTime get renewalAt; String get accountId; SnAccount? get account; bool get isAvailable; double get finalPrice; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
String get id; DateTime get begunAt; DateTime? get endedAt; String get identifier; bool get isActive; bool get isFreeTrial; int get status; String? get paymentMethod; Map<String, dynamic>? get paymentDetails; double? get basePrice; String? get couponId; dynamic get coupon; DateTime? get renewalAt; String get accountId; SnAccount? get account; bool get isAvailable; double? get finalPrice; DateTime get createdAt; DateTime get updatedAt; DateTime? get deletedAt;
/// Create a copy of SnWalletSubscription
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@ -595,7 +595,7 @@ abstract mixin class $SnWalletSubscriptionCopyWith<$Res> {
factory $SnWalletSubscriptionCopyWith(SnWalletSubscription value, $Res Function(SnWalletSubscription) _then) = _$SnWalletSubscriptionCopyWithImpl;
@useResult
$Res call({
String id, DateTime begunAt, DateTime endedAt, String identifier, bool isActive, bool isFreeTrial, int status, String paymentMethod, Map<String, dynamic> paymentDetails, double basePrice, String? couponId, dynamic coupon, DateTime renewalAt, String accountId, SnAccount? account, bool isAvailable, double finalPrice, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
String id, DateTime begunAt, DateTime? endedAt, String identifier, bool isActive, bool isFreeTrial, int status, String? paymentMethod, Map<String, dynamic>? paymentDetails, double? basePrice, String? couponId, dynamic coupon, DateTime? renewalAt, String accountId, SnAccount? account, bool isAvailable, double? finalPrice, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
@ -612,26 +612,26 @@ class _$SnWalletSubscriptionCopyWithImpl<$Res>
/// Create a copy of SnWalletSubscription
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? begunAt = null,Object? endedAt = null,Object? identifier = null,Object? isActive = null,Object? isFreeTrial = null,Object? status = null,Object? paymentMethod = null,Object? paymentDetails = null,Object? basePrice = null,Object? couponId = freezed,Object? coupon = freezed,Object? renewalAt = null,Object? accountId = null,Object? account = freezed,Object? isAvailable = null,Object? finalPrice = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? begunAt = null,Object? endedAt = freezed,Object? identifier = null,Object? isActive = null,Object? isFreeTrial = null,Object? status = null,Object? paymentMethod = freezed,Object? paymentDetails = freezed,Object? basePrice = freezed,Object? couponId = freezed,Object? coupon = freezed,Object? renewalAt = freezed,Object? accountId = null,Object? account = freezed,Object? isAvailable = null,Object? finalPrice = 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,begunAt: null == begunAt ? _self.begunAt : begunAt // ignore: cast_nullable_to_non_nullable
as DateTime,endedAt: null == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime,identifier: null == identifier ? _self.identifier : identifier // ignore: cast_nullable_to_non_nullable
as DateTime,endedAt: freezed == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,identifier: null == identifier ? _self.identifier : identifier // ignore: cast_nullable_to_non_nullable
as String,isActive: null == isActive ? _self.isActive : isActive // ignore: cast_nullable_to_non_nullable
as bool,isFreeTrial: null == isFreeTrial ? _self.isFreeTrial : isFreeTrial // ignore: cast_nullable_to_non_nullable
as bool,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as int,paymentMethod: null == paymentMethod ? _self.paymentMethod : paymentMethod // ignore: cast_nullable_to_non_nullable
as String,paymentDetails: null == paymentDetails ? _self.paymentDetails : paymentDetails // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,basePrice: null == basePrice ? _self.basePrice : basePrice // ignore: cast_nullable_to_non_nullable
as double,couponId: freezed == couponId ? _self.couponId : couponId // ignore: cast_nullable_to_non_nullable
as int,paymentMethod: freezed == paymentMethod ? _self.paymentMethod : paymentMethod // ignore: cast_nullable_to_non_nullable
as String?,paymentDetails: freezed == paymentDetails ? _self.paymentDetails : paymentDetails // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,basePrice: freezed == basePrice ? _self.basePrice : basePrice // ignore: cast_nullable_to_non_nullable
as double?,couponId: freezed == couponId ? _self.couponId : couponId // ignore: cast_nullable_to_non_nullable
as String?,coupon: freezed == coupon ? _self.coupon : coupon // ignore: cast_nullable_to_non_nullable
as dynamic,renewalAt: null == renewalAt ? _self.renewalAt : renewalAt // ignore: cast_nullable_to_non_nullable
as DateTime,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as dynamic,renewalAt: freezed == renewalAt ? _self.renewalAt : renewalAt // ignore: cast_nullable_to_non_nullable
as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,isAvailable: null == isAvailable ? _self.isAvailable : isAvailable // ignore: cast_nullable_to_non_nullable
as bool,finalPrice: null == finalPrice ? _self.finalPrice : finalPrice // ignore: cast_nullable_to_non_nullable
as double,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as bool,finalPrice: freezed == finalPrice ? _self.finalPrice : finalPrice // ignore: cast_nullable_to_non_nullable
as double?,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?,
@ -657,32 +657,34 @@ $SnAccountCopyWith<$Res>? get account {
@JsonSerializable()
class _SnWalletSubscription implements SnWalletSubscription {
const _SnWalletSubscription({required this.id, required this.begunAt, required this.endedAt, required this.identifier, required this.isActive, required this.isFreeTrial, required this.status, required this.paymentMethod, required final Map<String, dynamic> paymentDetails, required this.basePrice, required this.couponId, required this.coupon, required this.renewalAt, required this.accountId, required this.account, required this.isAvailable, required this.finalPrice, required this.createdAt, required this.updatedAt, required this.deletedAt}): _paymentDetails = paymentDetails;
const _SnWalletSubscription({required this.id, required this.begunAt, required this.endedAt, required this.identifier, this.isActive = true, this.isFreeTrial = false, this.status = 1, required this.paymentMethod, required final Map<String, dynamic>? paymentDetails, required this.basePrice, required this.couponId, required this.coupon, required this.renewalAt, required this.accountId, required this.account, this.isAvailable = true, required this.finalPrice, required this.createdAt, required this.updatedAt, required this.deletedAt}): _paymentDetails = paymentDetails;
factory _SnWalletSubscription.fromJson(Map<String, dynamic> json) => _$SnWalletSubscriptionFromJson(json);
@override final String id;
@override final DateTime begunAt;
@override final DateTime endedAt;
@override final DateTime? endedAt;
@override final String identifier;
@override final bool isActive;
@override final bool isFreeTrial;
@override final int status;
@override final String paymentMethod;
final Map<String, dynamic> _paymentDetails;
@override Map<String, dynamic> get paymentDetails {
@override@JsonKey() final bool isActive;
@override@JsonKey() final bool isFreeTrial;
@override@JsonKey() final int status;
@override final String? paymentMethod;
final Map<String, dynamic>? _paymentDetails;
@override Map<String, dynamic>? get paymentDetails {
final value = _paymentDetails;
if (value == null) return null;
if (_paymentDetails is EqualUnmodifiableMapView) return _paymentDetails;
// ignore: implicit_dynamic_type
return EqualUnmodifiableMapView(_paymentDetails);
return EqualUnmodifiableMapView(value);
}
@override final double basePrice;
@override final double? basePrice;
@override final String? couponId;
@override final dynamic coupon;
@override final DateTime renewalAt;
@override final DateTime? renewalAt;
@override final String accountId;
@override final SnAccount? account;
@override final bool isAvailable;
@override final double finalPrice;
@override@JsonKey() final bool isAvailable;
@override final double? finalPrice;
@override final DateTime createdAt;
@override final DateTime updatedAt;
@override final DateTime? deletedAt;
@ -720,7 +722,7 @@ abstract mixin class _$SnWalletSubscriptionCopyWith<$Res> implements $SnWalletSu
factory _$SnWalletSubscriptionCopyWith(_SnWalletSubscription value, $Res Function(_SnWalletSubscription) _then) = __$SnWalletSubscriptionCopyWithImpl;
@override @useResult
$Res call({
String id, DateTime begunAt, DateTime endedAt, String identifier, bool isActive, bool isFreeTrial, int status, String paymentMethod, Map<String, dynamic> paymentDetails, double basePrice, String? couponId, dynamic coupon, DateTime renewalAt, String accountId, SnAccount? account, bool isAvailable, double finalPrice, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
String id, DateTime begunAt, DateTime? endedAt, String identifier, bool isActive, bool isFreeTrial, int status, String? paymentMethod, Map<String, dynamic>? paymentDetails, double? basePrice, String? couponId, dynamic coupon, DateTime? renewalAt, String accountId, SnAccount? account, bool isAvailable, double? finalPrice, DateTime createdAt, DateTime updatedAt, DateTime? deletedAt
});
@ -737,26 +739,26 @@ class __$SnWalletSubscriptionCopyWithImpl<$Res>
/// Create a copy of SnWalletSubscription
/// with the given fields replaced by the non-null parameter values.
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? begunAt = null,Object? endedAt = null,Object? identifier = null,Object? isActive = null,Object? isFreeTrial = null,Object? status = null,Object? paymentMethod = null,Object? paymentDetails = null,Object? basePrice = null,Object? couponId = freezed,Object? coupon = freezed,Object? renewalAt = null,Object? accountId = null,Object? account = freezed,Object? isAvailable = null,Object? finalPrice = null,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? begunAt = null,Object? endedAt = freezed,Object? identifier = null,Object? isActive = null,Object? isFreeTrial = null,Object? status = null,Object? paymentMethod = freezed,Object? paymentDetails = freezed,Object? basePrice = freezed,Object? couponId = freezed,Object? coupon = freezed,Object? renewalAt = freezed,Object? accountId = null,Object? account = freezed,Object? isAvailable = null,Object? finalPrice = freezed,Object? createdAt = null,Object? updatedAt = null,Object? deletedAt = freezed,}) {
return _then(_SnWalletSubscription(
id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
as String,begunAt: null == begunAt ? _self.begunAt : begunAt // ignore: cast_nullable_to_non_nullable
as DateTime,endedAt: null == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime,identifier: null == identifier ? _self.identifier : identifier // ignore: cast_nullable_to_non_nullable
as DateTime,endedAt: freezed == endedAt ? _self.endedAt : endedAt // ignore: cast_nullable_to_non_nullable
as DateTime?,identifier: null == identifier ? _self.identifier : identifier // ignore: cast_nullable_to_non_nullable
as String,isActive: null == isActive ? _self.isActive : isActive // ignore: cast_nullable_to_non_nullable
as bool,isFreeTrial: null == isFreeTrial ? _self.isFreeTrial : isFreeTrial // ignore: cast_nullable_to_non_nullable
as bool,status: null == status ? _self.status : status // ignore: cast_nullable_to_non_nullable
as int,paymentMethod: null == paymentMethod ? _self.paymentMethod : paymentMethod // ignore: cast_nullable_to_non_nullable
as String,paymentDetails: null == paymentDetails ? _self._paymentDetails : paymentDetails // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>,basePrice: null == basePrice ? _self.basePrice : basePrice // ignore: cast_nullable_to_non_nullable
as double,couponId: freezed == couponId ? _self.couponId : couponId // ignore: cast_nullable_to_non_nullable
as int,paymentMethod: freezed == paymentMethod ? _self.paymentMethod : paymentMethod // ignore: cast_nullable_to_non_nullable
as String?,paymentDetails: freezed == paymentDetails ? _self._paymentDetails : paymentDetails // ignore: cast_nullable_to_non_nullable
as Map<String, dynamic>?,basePrice: freezed == basePrice ? _self.basePrice : basePrice // ignore: cast_nullable_to_non_nullable
as double?,couponId: freezed == couponId ? _self.couponId : couponId // ignore: cast_nullable_to_non_nullable
as String?,coupon: freezed == coupon ? _self.coupon : coupon // ignore: cast_nullable_to_non_nullable
as dynamic,renewalAt: null == renewalAt ? _self.renewalAt : renewalAt // ignore: cast_nullable_to_non_nullable
as DateTime,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as dynamic,renewalAt: freezed == renewalAt ? _self.renewalAt : renewalAt // ignore: cast_nullable_to_non_nullable
as DateTime?,accountId: null == accountId ? _self.accountId : accountId // ignore: cast_nullable_to_non_nullable
as String,account: freezed == account ? _self.account : account // ignore: cast_nullable_to_non_nullable
as SnAccount?,isAvailable: null == isAvailable ? _self.isAvailable : isAvailable // ignore: cast_nullable_to_non_nullable
as bool,finalPrice: null == finalPrice ? _self.finalPrice : finalPrice // ignore: cast_nullable_to_non_nullable
as double,createdAt: null == createdAt ? _self.createdAt : createdAt // ignore: cast_nullable_to_non_nullable
as bool,finalPrice: freezed == finalPrice ? _self.finalPrice : finalPrice // ignore: cast_nullable_to_non_nullable
as double?,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?,

View File

@ -106,24 +106,30 @@ _SnWalletSubscription _$SnWalletSubscriptionFromJson(
) => _SnWalletSubscription(
id: json['id'] as String,
begunAt: DateTime.parse(json['begun_at'] as String),
endedAt: DateTime.parse(json['ended_at'] as String),
endedAt:
json['ended_at'] == null
? null
: DateTime.parse(json['ended_at'] as String),
identifier: json['identifier'] as String,
isActive: json['is_active'] as bool,
isFreeTrial: json['is_free_trial'] as bool,
status: (json['status'] as num).toInt(),
paymentMethod: json['payment_method'] as String,
paymentDetails: json['payment_details'] as Map<String, dynamic>,
basePrice: (json['base_price'] as num).toDouble(),
isActive: json['is_active'] as bool? ?? true,
isFreeTrial: json['is_free_trial'] as bool? ?? false,
status: (json['status'] as num?)?.toInt() ?? 1,
paymentMethod: json['payment_method'] as String?,
paymentDetails: json['payment_details'] as Map<String, dynamic>?,
basePrice: (json['base_price'] as num?)?.toDouble(),
couponId: json['coupon_id'] as String?,
coupon: json['coupon'],
renewalAt: DateTime.parse(json['renewal_at'] as String),
renewalAt:
json['renewal_at'] == null
? null
: DateTime.parse(json['renewal_at'] as String),
accountId: json['account_id'] as String,
account:
json['account'] == null
? null
: SnAccount.fromJson(json['account'] as Map<String, dynamic>),
isAvailable: json['is_available'] as bool,
finalPrice: (json['final_price'] as num).toDouble(),
isAvailable: json['is_available'] as bool? ?? true,
finalPrice: (json['final_price'] as num?)?.toDouble(),
createdAt: DateTime.parse(json['created_at'] as String),
updatedAt: DateTime.parse(json['updated_at'] as String),
deletedAt:
@ -137,7 +143,7 @@ Map<String, dynamic> _$SnWalletSubscriptionToJson(
) => <String, dynamic>{
'id': instance.id,
'begun_at': instance.begunAt.toIso8601String(),
'ended_at': instance.endedAt.toIso8601String(),
'ended_at': instance.endedAt?.toIso8601String(),
'identifier': instance.identifier,
'is_active': instance.isActive,
'is_free_trial': instance.isFreeTrial,
@ -147,7 +153,7 @@ Map<String, dynamic> _$SnWalletSubscriptionToJson(
'base_price': instance.basePrice,
'coupon_id': instance.couponId,
'coupon': instance.coupon,
'renewal_at': instance.renewalAt.toIso8601String(),
'renewal_at': instance.renewalAt?.toIso8601String(),
'account_id': instance.accountId,
'account': instance.account?.toJson(),
'is_available': instance.isAvailable,

View File

@ -9,6 +9,7 @@ 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/services/time.dart';
import 'package:island/widgets/account/leveling_progress.dart';
import 'package:island/widgets/alert.dart';
import 'package:island/widgets/app_scaffold.dart';
@ -51,7 +52,7 @@ class LevelingScreen extends HookConsumerWidget {
// Level Stairs Graph
Text(
'Level Progress',
'levelProgress'.tr(),
style: Theme.of(
context,
).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold),
@ -79,12 +80,12 @@ class LevelingScreen extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Unlocked Features',
'unlockedFeatures'.tr(),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Gap(8),
Text(
'Features unlocked at your current level will be displayed here.',
'unlockedFeaturesDescription'.tr(),
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
@ -252,7 +253,7 @@ class LevelingScreen extends HookConsumerWidget {
),
const Gap(8),
Text(
'Stellar Membership',
'stellarMembership'.tr(),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
],
@ -265,7 +266,7 @@ class LevelingScreen extends HookConsumerWidget {
],
Text(
isActive ? 'Upgrade Your Plan' : 'Choose Your Plan',
isActive ? 'upgradeYourPlan'.tr() : 'chooseYourPlan'.tr(),
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
),
const Gap(12),
@ -299,15 +300,16 @@ class LevelingScreen extends HookConsumerWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Current: $tierName',
'currentMembership'.tr(args: [tierName]),
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w600,
color: tierColor,
),
),
if (membership.endedAt != null)
Text(
'Expires: ${_formatDate(membership.endedAt)}',
'membershipExpires'.tr(args: [membership.endedAt!.formatSystem()]),
style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
),
@ -328,31 +330,31 @@ class LevelingScreen extends HookConsumerWidget {
final tiers = [
{
'id': 'solian.stellar.primary',
'name': 'Stellar',
'price': '10 NS\$ per month',
'name': 'membershipTierStellar'.tr(),
'price': 'membershipPriceStellar'.tr(),
'features': [
'Basic features',
'Priority support',
'Ad-free experience',
'membershipFeatureBasic'.tr(),
'membershipFeaturePrioritySupport'.tr(),
'membershipFeatureAdFree'.tr(),
],
'color': Colors.blue,
},
{
'id': 'solian.stellar.nova',
'name': 'Nova',
'price': '20 NS\$ per month',
'name': 'membershipTierNova'.tr(),
'price': 'membershipPriceNova'.tr(),
'features': [
'All Primary features',
'Advanced customization',
'Early access',
'membershipFeatureAllPrimary'.tr(),
'membershipFeatureAdvancedCustomization'.tr(),
'membershipFeatureEarlyAccess'.tr(),
],
'color': Colors.purple,
},
{
'id': 'solian.stellar.supernova',
'name': 'Supernova',
'price': '30 NS\$ per month',
'features': ['All Nova features', 'Exclusive content', 'VIP support'],
'name': 'membershipTierSupernova'.tr(),
'price': 'membershipPriceSupernova'.tr(),
'features': ['membershipFeatureAllNova'.tr(), 'membershipFeatureExclusiveContent'.tr(), 'membershipFeatureVipSupport'.tr()],
'color': Colors.orange,
},
];
@ -432,7 +434,7 @@ class LevelingScreen extends HookConsumerWidget {
borderRadius: BorderRadius.circular(4),
),
child: Text(
'CURRENT',
'membershipCurrentBadge'.tr(),
style: TextStyle(
fontSize: 10,
fontWeight: FontWeight.bold,
@ -476,13 +478,13 @@ class LevelingScreen extends HookConsumerWidget {
String _getMembershipTierName(String identifier) {
switch (identifier) {
case 'solian.stellar.primary':
return 'Primary';
return 'membershipTierStellar'.tr();
case 'solian.stellar.nova':
return 'Nova';
return 'membershipTierNova'.tr();
case 'solian.stellar.supernova':
return 'Supernova';
return 'membershipTierSupernova'.tr();
default:
return 'Unknown';
return 'membershipTierUnknown'.tr();
}
}
@ -499,10 +501,6 @@ class LevelingScreen extends HookConsumerWidget {
}
}
String _formatDate(DateTime date) {
return '${date.day}/${date.month}/${date.year}';
}
Future<void> _purchaseMembership(
BuildContext context,
WidgetRef ref,
@ -538,21 +536,23 @@ class LevelingScreen extends HookConsumerWidget {
enableBiometric: true,
);
if (context.mounted) showLoadingModal(context);
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,
),
await client.post(
'/subscriptions/order/handle',
data: {'order_id': paidOrder.id},
);
ref.read(userInfoProvider.notifier).fetchUser();
if (context.mounted) {
showSnackBar(context, 'membershipPurchaseSuccess'.tr());
}
}
} catch (err) {
if (context.mounted) hideLoadingModal(context);
showErrorAlert(err);
} finally {
if (context.mounted) hideLoadingModal(context);
}
}
}

View File

@ -12,6 +12,7 @@ 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';
import 'package:styled_widget/styled_widget.dart';
class PaymentOverlay extends HookConsumerWidget {
final SnWalletOrder order;
@ -278,12 +279,7 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
_isPinMode = true;
});
if (message != null && message.isNotEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
backgroundColor: Theme.of(context).colorScheme.error,
),
);
showSnackBar(context, message);
}
}
@ -423,21 +419,10 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
Widget _buildBiometricAuth() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.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),
Icon(Symbols.fingerprint, size: 48),
const Gap(16),
Text(
'useBiometricToConfirm'.tr(),
style: Theme.of(
@ -445,15 +430,15 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.w500),
textAlign: TextAlign.center,
),
const Gap(16),
Text(
'touchSensorToAuthenticate'.tr(),
'The biometric data will only be processed on your device',
style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Theme.of(context).colorScheme.onSurfaceVariant,
fontSize: 11,
),
textAlign: TextAlign.center,
),
const Gap(32),
).opacity(0.75),
const Gap(28),
ElevatedButton.icon(
onPressed: _authenticateWithBiometric,
icon: const Icon(Symbols.fingerprint),
@ -462,13 +447,12 @@ class _PaymentContentState extends ConsumerState<_PaymentContent> {
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
),
),
const Gap(16),
TextButton(
onPressed: () => _fallbackToPinMode(null),
child: Text('usePinInstead'.tr()),
),
],
);
).center();
}
Widget _buildActionButtons() {