mirror of
https://github.com/jonasbark/swiftcontrol.git
synced 2026-02-18 00:17:40 +01:00
221 lines
8.5 KiB
Dart
221 lines
8.5 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:bike_control/gen/l10n.dart';
|
|
import 'package:bike_control/utils/i18n_extension.dart';
|
|
import 'package:bike_control/utils/iap/iap_manager.dart';
|
|
import 'package:bike_control/widgets/ui/small_progress_indicator.dart';
|
|
import 'package:bike_control/widgets/ui/toast.dart';
|
|
import 'package:shadcn_flutter/shadcn_flutter.dart';
|
|
|
|
/// Widget to display IAP status and allow purchases
|
|
class IAPStatusWidget extends StatefulWidget {
|
|
final bool small;
|
|
const IAPStatusWidget({super.key, required this.small});
|
|
|
|
@override
|
|
State<IAPStatusWidget> createState() => _IAPStatusWidgetState();
|
|
}
|
|
|
|
class _IAPStatusWidgetState extends State<IAPStatusWidget> {
|
|
bool _isPurchasing = false;
|
|
bool _isSmall = false;
|
|
|
|
@override
|
|
void initState() {
|
|
super.initState();
|
|
_isSmall = widget.small;
|
|
}
|
|
|
|
@override
|
|
void didUpdateWidget(covariant IAPStatusWidget oldWidget) {
|
|
super.didUpdateWidget(oldWidget);
|
|
if (oldWidget.small != widget.small) {
|
|
setState(() {
|
|
_isSmall = widget.small;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final iapManager = IAPManager.instance;
|
|
final isTrialExpired = iapManager.isTrialExpired;
|
|
if (isTrialExpired) {
|
|
_isSmall = false;
|
|
}
|
|
final trialDaysRemaining = iapManager.trialDaysRemaining;
|
|
final commandsRemaining = iapManager.commandsRemainingToday;
|
|
final dailyCommandCount = iapManager.dailyCommandCount;
|
|
|
|
return Button(
|
|
onPressed: _isSmall
|
|
? () {
|
|
setState(() {
|
|
_isSmall = false;
|
|
});
|
|
}
|
|
: _handlePurchase,
|
|
style: ButtonStyle.card().withBackgroundColor(
|
|
color: Theme.of(context).colorScheme.muted,
|
|
hoverColor: Theme.of(context).colorScheme.primaryForeground,
|
|
),
|
|
child: AnimatedContainer(
|
|
duration: Duration(milliseconds: 700),
|
|
width: double.infinity,
|
|
child: ValueListenableBuilder(
|
|
valueListenable: IAPManager.instance.isPurchased,
|
|
builder: (context, isPurchased, child) {
|
|
return Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
if (isPurchased) ...[
|
|
Row(
|
|
children: [
|
|
Icon(Icons.check_circle, color: Colors.green),
|
|
const SizedBox(width: 8),
|
|
Text(
|
|
AppLocalizations.of(context).fullVersion,
|
|
style: TextStyle(
|
|
color: Colors.green,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
] else if (!isTrialExpired) ...[
|
|
if (!Platform.isAndroid)
|
|
Basic(
|
|
leadingAlignment: Alignment.centerLeft,
|
|
leading: Icon(Icons.access_time, color: Colors.blue),
|
|
title: Text(AppLocalizations.of(context).trialPeriodActive(trialDaysRemaining)),
|
|
subtitle: _isSmall
|
|
? null
|
|
: Text(AppLocalizations.of(context).trialPeriodDescription(IAPManager.dailyCommandLimit)),
|
|
trailing: _isSmall ? Icon(Icons.expand_more) : null,
|
|
)
|
|
else
|
|
Basic(
|
|
leadingAlignment: Alignment.centerLeft,
|
|
leading: Icon(Icons.lock),
|
|
title: Text(AppLocalizations.of(context).trialPeriodActive(trialDaysRemaining)),
|
|
subtitle: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
spacing: 6,
|
|
children: [
|
|
SizedBox(),
|
|
Text(
|
|
commandsRemaining >= 0
|
|
? context.i18n
|
|
.commandsRemainingToday(commandsRemaining, IAPManager.dailyCommandLimit)
|
|
.replaceAll(
|
|
'${IAPManager.dailyCommandLimit}/${IAPManager.dailyCommandLimit}',
|
|
IAPManager.dailyCommandLimit.toString(),
|
|
)
|
|
: AppLocalizations.of(
|
|
context,
|
|
).dailyLimitReached(dailyCommandCount, IAPManager.dailyCommandLimit),
|
|
).small,
|
|
if (commandsRemaining >= 0)
|
|
SizedBox(
|
|
width: 300,
|
|
child: LinearProgressIndicator(
|
|
value: dailyCommandCount.toDouble() / IAPManager.dailyCommandLimit.toDouble(),
|
|
backgroundColor: Colors.gray[300],
|
|
color: commandsRemaining > 0 ? Colors.orange : Colors.red,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
trailing: _isSmall ? Icon(Icons.expand_more) : null,
|
|
trailingAlignment: Alignment.centerRight,
|
|
),
|
|
] else ...[
|
|
Basic(
|
|
leadingAlignment: Alignment.centerLeft,
|
|
leading: Icon(Icons.lock),
|
|
title: Text(AppLocalizations.of(context).trialExpired(IAPManager.dailyCommandLimit)),
|
|
trailing: _isSmall ? Icon(Icons.expand_more) : null,
|
|
trailingAlignment: Alignment.centerRight,
|
|
subtitle: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
spacing: 6,
|
|
children: [
|
|
SizedBox(),
|
|
Text(
|
|
commandsRemaining >= 0
|
|
? context.i18n.commandsRemainingToday(commandsRemaining, IAPManager.dailyCommandLimit)
|
|
: AppLocalizations.of(
|
|
context,
|
|
).dailyLimitReached(dailyCommandCount, IAPManager.dailyCommandLimit),
|
|
).small,
|
|
if (commandsRemaining >= 0)
|
|
SizedBox(
|
|
width: 300,
|
|
child: LinearProgressIndicator(
|
|
value: dailyCommandCount.toDouble() / IAPManager.dailyCommandLimit.toDouble(),
|
|
backgroundColor: Colors.gray[300],
|
|
color: commandsRemaining > 0 ? Colors.orange : Colors.red,
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
],
|
|
if (!IAPManager.instance.isPurchased.value && !_isSmall) ...[
|
|
const SizedBox(height: 16),
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 42.0),
|
|
child: PrimaryButton(
|
|
onPressed: _isPurchasing ? null : _handlePurchase,
|
|
leading: Icon(Icons.star),
|
|
child: _isPurchasing
|
|
? Row(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
mainAxisSize: MainAxisSize.min,
|
|
children: [
|
|
SmallProgressIndicator(),
|
|
const SizedBox(width: 8),
|
|
Text('Processing...'),
|
|
],
|
|
)
|
|
: Text(AppLocalizations.of(context).unlockFullVersion),
|
|
),
|
|
),
|
|
Padding(
|
|
padding: const EdgeInsets.only(left: 42.0, top: 8.0),
|
|
child: Text(AppLocalizations.of(context).fullVersionDescription).xSmall,
|
|
),
|
|
],
|
|
],
|
|
);
|
|
},
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
Future<void> _handlePurchase() async {
|
|
setState(() {
|
|
_isPurchasing = true;
|
|
});
|
|
|
|
try {
|
|
await IAPManager.instance.purchaseFullVersion();
|
|
} catch (e) {
|
|
if (mounted) {
|
|
buildToast(
|
|
context,
|
|
title: 'Error',
|
|
subtitle: 'An error occurred: $e',
|
|
);
|
|
}
|
|
} finally {
|
|
if (mounted) {
|
|
setState(() {
|
|
_isPurchasing = false;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|